perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fail closed on invalid repo imports

alice 27423e57 fde9773e

+55 -1
+8 -1
lib/ATProto/PDS/API/Repo.pm
··· 183 183 my $car_bytes = $c->req->body // q(); 184 184 xrpc_error(400, 'InvalidRequest', 'Repo import requires a CAR payload') 185 185 unless length $car_bytes; 186 - $c->repo_manager->import_repo_car($account, $car_bytes); 186 + eval { 187 + $c->repo_manager->import_repo_car($account, $car_bytes); 188 + 1; 189 + } or do { 190 + my $err = $@; 191 + die $err if ref($err) eq 'HASH'; 192 + xrpc_error(400, 'InvalidRepoImport', 'Repo import CAR was invalid'); 193 + }; 187 194 return {}; 188 195 }); 189 196 }
+47
t/import-repo.t
··· 89 89 $t->post_ok('/xrpc/com.atproto.repo.importRepo' => { 90 90 Authorization => "Bearer $access", 91 91 'Content-Type' => 'application/vnd.ipld.car', 92 + } => 'not-a-car')->status_is(400) 93 + ->json_is('/error' => 'InvalidRepoImport'); 94 + 95 + $t->get_ok('/xrpc/com.atproto.repo.listRecords' => form => { 96 + repo => $did, 97 + collection => 'app.bsky.feed.post', 98 + })->status_is(200); 99 + is(scalar @{ $t->tx->res->json->{records} || [] }, 2, 'invalid import leaves repo state untouched'); 100 + 101 + $t->post_ok('/xrpc/com.atproto.repo.importRepo' => { 102 + Authorization => "Bearer $access", 103 + 'Content-Type' => 'application/vnd.ipld.car', 92 104 } => $snapshot)->status_is(200); 93 105 94 106 $t->get_ok('/xrpc/com.atproto.repo.listRecords' => form => { ··· 101 113 my ($restored) = grep { ($_->{uri} // q()) eq "at://$did/app.bsky.feed.post/before-import" } @$records; 102 114 ok($restored, 'imported repo keeps the earlier record URI'); 103 115 is($restored->{value}{text}, 'state before import', 'imported repo restores the earlier record body'); 116 + ok( 117 + !(scalar grep { ($_->{uri} // q()) eq "at://$did/app.bsky.feed.post/after-import" } @$records), 118 + 'importRepo drops writes that happened after the imported snapshot', 119 + ); 120 + 121 + my $disabled_tmp = tempdir(CLEANUP => 1); 122 + my $disabled_app = ATProto::PDS->new( 123 + project_root => $root, 124 + settings => { 125 + base_url => 'http://127.0.0.1:7755', 126 + service_handle_domain => 'example.test', 127 + service_did_method => 'did:web', 128 + accepting_imports => 0, 129 + jwt_secret => 'import-disabled-secret', 130 + admin_password => 'admin-secret', 131 + data_dir => File::Spec->catdir($disabled_tmp, 'data'), 132 + db_path => File::Spec->catfile($disabled_tmp, 'perlsky.sqlite'), 133 + }, 134 + ); 135 + my $disabled_t = Test::Mojo->new($disabled_app); 136 + 137 + $disabled_t->post_ok('/xrpc/com.atproto.server.createAccount' => json => { 138 + handle => 'carol.example.test', 139 + email => 'carol@example.test', 140 + password => 'hunter22', 141 + })->status_is(200); 142 + 143 + my $disabled_access = $disabled_t->tx->res->json->{accessJwt}; 144 + 145 + $disabled_t->post_ok('/xrpc/com.atproto.repo.importRepo' => { 146 + Authorization => "Bearer $disabled_access", 147 + 'Content-Type' => 'application/vnd.ipld.car', 148 + } => 'x')->status_is(400) 149 + ->json_is('/error' => 'InvalidRequest') 150 + ->json_is('/message' => 'Service is not accepting repo imports'); 104 151 105 152 done_testing;