Skip to content

Decorator-level DI guards read as unguarded: Depends() in a decorator emits no DEPENDS_ON #429

Description

@zaebee

Found by an independent review of #428 (which fixed the module-level and class-body halves of #416).

The gap

_walk returns as soon as it sees a decorated_definition, handing the node to _handle_decorated_definition. That method descends into the definition's grandchildren and never into the decorator nodes themselves. So any call written inside a decorator expression produces no edge at all — on a function, a method, a class, or a nested class.

@deco(Foo())          # the Foo() call is invisible
def f(): ...

Verified absent both before and after #428.

Why it matters concretely

owner-api, app/domains/admin/system.py:

@router.post("/showcase/reset", dependencies=[Depends(get_current_active_superuser)])
async def trigger_showcase_reset(...):

trigger_showcase_reset has no DEPENDS_ON edge. cgis audit (see audit.py) reads DEPENDS_ON as the evidence that an endpoint is guarded, so this endpoint audits as unguarded when it is in fact superuser-only. Any route that declares its guard at the decorator rather than in the signature has the same false negative.

Not simply "walk the decorators too"

The CALLS half is mechanical. The DI half is a design question, which is why it is a separate issue rather than a line in #428:

  • Depends(get_current_active_superuser) inside @router.post(...) is lexically at module level but semantically guards the decorated function. Attributing it to the module would be honest about location and useless for the audit; attributing it to the function is what the audit wants, and is the first place in the extractor where an edge's source is not its lexical owner.
  • 2026-06-11-fastapi-di-edges-design.md §6 puts class-body DI aliases out of scope. Decorator DI is not that case, but the spec does not say what it is either — it needs a decision written down before code.

Suggested shape

  1. Walk decorator expressions for CALLS, sourced at the decorated definition (it is the nearest owner, and a decorator has no other).
  2. Decide, and record in the DI spec, whether Depends/Security in a decorator emits DEPENDS_ON sourced at the decorated function. Measure the audit's false-negative count on owner-api both ways before choosing.
  3. Re-run the audit false-negative count as the acceptance number.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions