# The C1 resource-in-community invariant (ultra-fuzz CHRONIC) is sealed by the # type system now, with one lint-guarded escape hatch. # # The raw by-id loaders (`get_thread_with_breadcrumb`, `get_post_for_edit`) # return their resource wrapped in `mt_db::queries::Unscoped`. The inner value # is private; the safe way out is `Unscoped::in_community(expected)`, which yields # it only when the resource's community matches the one the caller names. # `routes::scope::CommunityScope::resolve` is the sole such site and it passes the # community the URL slug resolved to, so a handler cannot serve a by-id community # resource without proving it belongs to that community. That check needs no lint: # it is enforced by the type. # # `Unscoped::into_inner_unchecked` unwraps with no community check, for callers # that have no slug to scope against (the trusted internal server-to-server API). # It is disallowed below so a `/p/{slug}/…` handler cannot reach for it by # accident; the few sanctioned sites carry a local `#[allow]`. # # This replaced a deny-list that enumerated every raw loader by hand and failed # open for any loader nobody remembered to add. The seal now fails closed: a new # by-id loader that returns `Unscoped` is bound automatically, and the only # lint entry is on the single unchecked-unwrap primitive. See `src/routes/scope.rs`. disallowed-methods = [ { path = "mt_db::queries::Unscoped::into_inner_unchecked", reason = "unwraps a community resource without the C1 scope check; a slug-scoped handler must use Unscoped::in_community via routes::scope::CommunityScope instead" }, ]