max / makenotwork
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
34 files changed,
+104 insertions,
-39 deletions
| @@ -5009,6 +5009,14 @@ | |||
| 5009 | 5009 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 5010 | 5010 | checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" | |
| 5011 | 5011 | ||
| 5012 | + | [[patch.unused]] | |
| 5013 | + | name = "synckit-client" | |
| 5014 | + | version = "0.6.0" | |
| 5015 | + | ||
| 5016 | + | [[patch.unused]] | |
| 5017 | + | name = "synckit-config" | |
| 5018 | + | version = "0.1.2" | |
| 5019 | + | ||
| 5012 | 5020 | [[patch.unused]] | |
| 5013 | 5021 | name = "kberg" | |
| 5014 | 5022 | version = "0.1.0" | |
| @@ -5020,11 +5028,3 @@ | |||
| 5020 | 5028 | [[patch.unused]] | |
| 5021 | 5029 | name = "supernote-push" | |
| 5022 | 5030 | version = "0.1.0" | |
| 5023 | - | ||
| 5024 | - | [[patch.unused]] | |
| 5025 | - | name = "synckit-client" | |
| 5026 | - | version = "0.6.0" | |
| 5027 | - | ||
| 5028 | - | [[patch.unused]] | |
| 5029 | - | name = "synckit-config" | |
| 5030 | - | version = "0.1.2" |
| @@ -1,24 +1,14 @@ | |||
| 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. | |
| 1 | + | # The C1 resource-in-community invariant is sealed by the type system, with one | |
| 2 | + | # lint-guarded escape hatch. `src/routes/scope.rs` is the canonical account of | |
| 3 | + | # why the seal exists and what it replaced; this file only says what the lint | |
| 4 | + | # entry below is for. | |
| 12 | 5 | # | |
| 13 | 6 | # `Unscoped::into_inner_unchecked` unwraps with no community check, for callers | |
| 14 | 7 | # that have no slug to scope against (the trusted internal server-to-server API). | |
| 15 | 8 | # 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`. | |
| 9 | + | # accident; the one sanctioned site, in `src/routes/internal.rs`, carries a local | |
| 10 | + | # `#[allow]`. It is the only lint entry because it is the only unchecked-unwrap | |
| 11 | + | # primitive: everything else is enforced by the type. | |
| 22 | 12 | disallowed-methods = [ | |
| 23 | 13 | { 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 | 14 | ] |
| @@ -1,7 +1,23 @@ | |||
| 1 | + | //! Flagging, auto-hide and the moderation actions that resolve them. | |
| 2 | + | //! | |
| 3 | + | //! Four properties, each security-relevant: | |
| 4 | + | //! | |
| 5 | + | //! - auto-hide threshold arithmetic: a post hides at the threshold, never below | |
| 6 | + | //! it, and never when the threshold is null | |
| 7 | + | //! - concurrency: `auto_hide_if_threshold_met` hides exactly once when two | |
| 8 | + | //! flaggers race, and endorsement toggles never duplicate under the same race | |
| 9 | + | //! - mod-log attribution: an auto-hide logs the System actor, not whichever | |
| 10 | + | //! flagger happened to cross the threshold | |
| 11 | + | //! - the write-level access gate on flagging: logged out, own post, platform | |
| 12 | + | //! suspended and muted are each rejected | |
| 13 | + | //! | |
| 14 | + | //! Restore is covered alongside removal because the two share the cascade: a | |
| 15 | + | //! restore must also resolve the flags, or the post rehides immediately. | |
| 16 | + | ||
| 1 | 17 | use crate::harness::TestHarness; | |
| 2 | 18 | use mt_core::types::BanType; | |
| 3 | 19 | ||
| 4 | - | // Auto-hide threshold tests | |
| 20 | + | // --- auto-hide threshold | |
| 5 | 21 | ||
| 6 | 22 | #[tokio::test] | |
| 7 | 23 | async fn auto_hide_threshold_removes_post_at_threshold() { |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! category writes, including the swap that maintains display order | |
| 2 | + | ||
| 1 | 3 | use super::{PgPool, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Create a new category in a community. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! community lifecycle: create, update, suspend, state transitions | |
| 2 | + | ||
| 1 | 3 | use super::{CommunityState, PgPool, Utc, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Create a new community and return its ID. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! endorsement toggle, idempotent per (user, post) under concurrent submits | |
| 2 | + | ||
| 1 | 3 | use super::{PgPool, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Toggle endorsement: insert if missing, delete if exists. Returns true if now endorsed. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! footnote inserts | |
| 2 | + | ||
| 1 | 3 | use super::{PgPool, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Insert a footnote on a post. Returns the footnote ID. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! image lifecycle from insert through to S3 purge marking | |
| 2 | + | ||
| 1 | 3 | use super::{PgPool, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Insert an uploaded image record. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! link preview inserts | |
| 2 | + | ||
| 1 | 3 | use super::{PgPool, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Insert a link preview for a post. Ignores duplicates. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! membership upserts, idempotent so a re-join never duplicates a row | |
| 2 | + | ||
| 1 | 3 | use super::{CommunityRole, PgPool, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Ensure a user has a membership in a community with the given role. |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! mention inserts, written as one batch per post | |
| 2 | + | ||
| 1 | 3 | use std::fmt::Write as _; | |
| 2 | 4 | ||
| 3 | 5 | use super::Uuid; |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! bans, mod-log entries and flag resolution | |
| 2 | + | ||
| 1 | 3 | use super::{BanType, DateTime, ModAction, ModActor, PgPool, Utc, Uuid}; | |
| 2 | 4 | ||
| 3 | 5 | /// Create or update a ban/mute. Returns the ban ID. |