fix(privacy): the instance's side of the reading of private studysets (ADR 0019)

An upload said to be encrypted must start as a sealed file does (HLKF,
version 1), so a readable file can't skip the PDF check by saying so. A
person's room counts the files a live asset row names and those claimed
within the week of grace, so a private studyset's rewrite near the limit
no longer fails for a week while pruning keeps the replaced files. Copy
reports are limited to 60 an hour per person. Tests: what goes with the
person, `counted` refused to anonymous people.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-22 08:45:01 +02:00
commit d9b5e06618
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -191,7 +191,9 @@ void main() {
'look into it, a little larger than 50 MB too, and no larger',
() async {
const size = AssetEndpoint.maxOtherBytes + 1;
final sealed = Uint8List(size)..fillRange(0, size, 7);
final sealed = Uint8List(size)
..fillRange(0, size, 7)
..setAll(0, [...utf8.encode('HLKF'), 1]);
final readable = Uint8List(size)..fillRange(0, size, 8);
const most =
AssetEndpoint.maxBytes + AssetEndpoint.encryptionOverhead;
@ -228,6 +230,25 @@ void main() {
},
);
test(
'keeps no file said to be encrypted that is no sealed file, as a '
'readable one would skip the PDF check',
() async {
const size = AssetEndpoint.maxOtherBytes + 1;
final readable = Uint8List(size)..fillRange(0, size, 9);
final wrongVersion = Uint8List(size)
..fillRange(0, size, 9)
..setAll(0, [...utf8.encode('HLKF'), 2]);
expect(await add(alice, readable, encrypted: true), isFalse);
expect(await add(alice, wrongVersion, encrypted: true), isFalse);
expect(
await add(alice, utf8.encode('HLKF'), encrypted: true),
isFalse,
);
},
);
test('finishes no upload that never came', () async {
expect(
await endpoints.asset.finishUpload(signedIn(alice), imageHash),
@ -488,6 +509,40 @@ void main() {
);
});
test(
'counts no file a live row no longer names once its week of grace '
'is over, as one a rewrite replaced (ADR 0019)',
() async {
final replaced = List.filled(6, 3);
final named = List.filled(3, 4);
String hash(List<int> bytes) => sha256.convert(bytes).toString();
final gone = await _assetIn(session, alice, hash(replaced));
await add(alice, replaced);
await _deleteAsset(session, alice, gone);
await _assetIn(session, alice, hash(named));
await add(alice, named);
final old = DateTime.now().toUtc().subtract(
const Duration(days: 8),
);
await _claimed(session, alice, hash(replaced), old);
await _claimed(session, alice, hash(named), old);
final usage = await endpoints.asset.usage(signedIn(alice));
expect(usage.usedBytes, 3);
},
);
test(
'counts a file no row names yet while it is recent, so uploads '
"without rows can't fill the room unnoticed",
() async {
await add(alice, List.filled(4, 5));
final usage = await endpoints.asset.usage(signedIn(alice));
expect(usage.usedBytes, 4);
},
);
test('tells how much room the files take, and may take', () async {
await add(alice, List.filled(4, 1));