| 1 |
# The C1 resource-in-community invariant (ultra-fuzz CHRONIC) is sealed by the |
| 2 |
# type system now, with one lint-guarded escape hatch. |
| 3 |
# |
| 4 |
# The raw by-id loaders (`get_thread_with_breadcrumb`, `get_post_for_edit`) |
| 5 |
# return their resource wrapped in `mt_db::queries::Unscoped<T>`. The inner value |
| 6 |
# is private; the safe way out is `Unscoped::in_community(expected)`, which yields |
| 7 |
# it only when the resource's community matches the one the caller names. |
| 8 |
# `routes::scope::CommunityScope::resolve` is the sole such site and it passes the |
| 9 |
# community the URL slug resolved to, so a handler cannot serve a by-id community |
| 10 |
# resource without proving it belongs to that community. That check needs no lint: |
| 11 |
# it is enforced by the type. |
| 12 |
# |
| 13 |
# `Unscoped::into_inner_unchecked` unwraps with no community check, for callers |
| 14 |
# that have no slug to scope against (the trusted internal server-to-server API). |
| 15 |
# It is disallowed below so a `/p/{slug}/…` handler cannot reach for it by |
| 16 |
# accident; the few sanctioned sites carry a local `#[allow]`. |
| 17 |
# |
| 18 |
# This replaced a deny-list that enumerated every raw loader by hand and failed |
| 19 |
# open for any loader nobody remembered to add. The seal now fails closed: a new |
| 20 |
# by-id loader that returns `Unscoped<T>` is bound automatically, and the only |
| 21 |
# lint entry is on the single unchecked-unwrap primitive. See `src/routes/scope.rs`. |
| 22 |
disallowed-methods = [ |
| 23 |
{ 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" }, |
| 24 |
] |
| 25 |
|