@@ -1928,14 +1928,107 @@ mod tests {
19281928 . unwrap ( ) ;
19291929 assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
19301930 let body = json_body ( resp) . await ;
1931- assert_eq ! ( body[ "count" ] , 1 , "pin for the real git object must be returned" ) ;
19321931 assert_eq ! (
1933- body[ "pins" ] [ 0 ] [ "sha256_hex" ] ,
1934- pinned_sha,
1932+ body[ "count" ] , 1 ,
1933+ "pin for the real git object must be returned"
1934+ ) ;
1935+ assert_eq ! (
1936+ body[ "pins" ] [ 0 ] [ "sha256_hex" ] , pinned_sha,
19351937 "returned pin must match the seeded object OID"
19361938 ) ;
19371939 }
19381940
1941+ #[ sqlx:: test]
1942+ async fn pins_list_excludes_quarantined_repos ( pool : PgPool ) {
1943+ use gitlawb_core:: identity:: Keypair ;
1944+
1945+ let owner = Keypair :: generate ( ) ;
1946+ let owner_did = owner. did ( ) . to_string ( ) ;
1947+ let fs_slug = owner_did. replace ( [ ':' , '/' ] , "_" ) ;
1948+ let short = owner_did. split ( ':' ) . next_back ( ) . unwrap ( ) . to_string ( ) ;
1949+
1950+ let state = test_state ( pool) . await ;
1951+
1952+ let fx = seed_cid_repos ( & fs_slug, & short, & [ "pinrepo" ] ) ;
1953+ let pinned_sha = fx. public_oid . clone ( ) ;
1954+ let pinned_cid = cid_for_oid ( & pinned_sha) ;
1955+
1956+ let repo = seed_repo ( & owner_did, "pinrepo" ) ;
1957+ state. db . create_repo ( & repo) . await . unwrap ( ) ;
1958+ state. db . set_repo_quarantine ( & repo. id , true ) . await . unwrap ( ) ;
1959+
1960+ state
1961+ . db
1962+ . record_pinned_cid ( & pinned_sha, & pinned_cid)
1963+ . await
1964+ . unwrap ( ) ;
1965+
1966+ let resp = pins_router ( & state)
1967+ . oneshot ( signed_get ( & owner, "/api/v1/ipfs/pins" ) )
1968+ . await
1969+ . unwrap ( ) ;
1970+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
1971+ let body = json_body ( resp) . await ;
1972+ assert_eq ! (
1973+ body[ "count" ] , 0 ,
1974+ "quarantined repo objects must be withheld from pin index"
1975+ ) ;
1976+ }
1977+
1978+ #[ sqlx:: test]
1979+ async fn pins_list_withholds_path_scoped_blobs ( pool : PgPool ) {
1980+ use crate :: db:: VisibilityMode ;
1981+ use gitlawb_core:: identity:: Keypair ;
1982+
1983+ let owner = Keypair :: generate ( ) ;
1984+ let owner_did = owner. did ( ) . to_string ( ) ;
1985+ let fs_slug = owner_did. replace ( [ ':' , '/' ] , "_" ) ;
1986+ let short = owner_did. split ( ':' ) . next_back ( ) . unwrap ( ) . to_string ( ) ;
1987+
1988+ let stranger = Keypair :: generate ( ) ;
1989+
1990+ let state = test_state ( pool) . await ;
1991+
1992+ let fx = seed_cid_repos ( & fs_slug, & short, & [ "pinrepo" ] ) ;
1993+ let public_sha = fx. public_oid . clone ( ) ;
1994+ let public_cid = cid_for_oid ( & public_sha) ;
1995+ let secret_sha = fx. secret_oid . clone ( ) ;
1996+ let secret_cid = cid_for_oid ( & secret_sha) ;
1997+
1998+ let repo = seed_repo ( & owner_did, "pinrepo" ) ;
1999+ state. db . create_repo ( & repo) . await . unwrap ( ) ;
2000+
2001+ state
2002+ . db
2003+ . set_visibility_rule ( & repo. id , "/secret/**" , VisibilityMode :: B , & [ ] , & owner_did)
2004+ . await
2005+ . unwrap ( ) ;
2006+
2007+ state
2008+ . db
2009+ . record_pinned_cid ( & public_sha, & public_cid)
2010+ . await
2011+ . unwrap ( ) ;
2012+ state
2013+ . db
2014+ . record_pinned_cid ( & secret_sha, & secret_cid)
2015+ . await
2016+ . unwrap ( ) ;
2017+
2018+ let resp = pins_router ( & state)
2019+ . oneshot ( signed_get ( & stranger, "/api/v1/ipfs/pins" ) )
2020+ . await
2021+ . unwrap ( ) ;
2022+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
2023+ let body = json_body ( resp) . await ;
2024+
2025+ assert_eq ! ( body[ "count" ] , 1 , "stranger sees only the public pin" ) ;
2026+ assert_eq ! (
2027+ body[ "pins" ] [ 0 ] [ "sha256_hex" ] , public_sha,
2028+ "stranger sees the public pin"
2029+ ) ;
2030+ }
2031+
19392032 // ---- #121: GET /api/v1/arweave/anchors auth and visibility gate ----
19402033
19412034 fn anchors_router ( state : & AppState ) -> Router {
@@ -2012,7 +2105,42 @@ mod tests {
20122105 . unwrap ( ) ;
20132106 assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
20142107 let body = json_body ( resp) . await ;
2015- assert_eq ! ( body[ "count" ] , 1 , "authenticated caller sees the seeded anchor" ) ;
2108+ assert_eq ! (
2109+ body[ "count" ] , 1 ,
2110+ "authenticated caller sees the seeded anchor"
2111+ ) ;
2112+ }
2113+
2114+ #[ sqlx:: test]
2115+ async fn anchors_global_denies_non_reader ( pool : PgPool ) {
2116+ use gitlawb_core:: identity:: Keypair ;
2117+
2118+ let state = test_state ( pool) . await ;
2119+ let owner = Keypair :: generate ( ) ;
2120+ let owner_did = owner. did ( ) . to_string ( ) ;
2121+ let stranger = Keypair :: generate ( ) ;
2122+ let repo_name = "privaterepo" ;
2123+ let owner_short = owner_did. split ( ':' ) . next_back ( ) . unwrap ( ) . to_string ( ) ;
2124+ let short_slug = format ! ( "{owner_short}/{repo_name}" ) ;
2125+
2126+ state
2127+ . db
2128+ . create_repo ( & seed_private_repo ( & owner_did, repo_name) )
2129+ . await
2130+ . unwrap ( ) ;
2131+
2132+ seed_anchor ( & state. db , & short_slug, & owner_did) . await ;
2133+
2134+ let resp = anchors_router ( & state)
2135+ . oneshot ( signed_get ( & stranger, "/api/v1/arweave/anchors" ) )
2136+ . await
2137+ . unwrap ( ) ;
2138+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
2139+ let body = json_body ( resp) . await ;
2140+ assert_eq ! (
2141+ body[ "count" ] , 0 ,
2142+ "non-reader is denied the private anchor in global listing"
2143+ ) ;
20162144 }
20172145
20182146 /// #121: /api/v1/arweave/anchors with ?repo= denies anonymous on private repo.
0 commit comments