Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions apps/server/src/guest/GuestShareService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts";
import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";
import * as ResourceCleanupService from "../orchestration-v2/ResourceCleanupService.ts";
import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";
import * as DocumentText from "../provider/DocumentText.ts";
import * as ProviderRegistry from "../provider/ProviderRegistry.ts";
import * as ServerSettings from "../serverSettings.ts";
import * as AnalyticsService from "../telemetry/AnalyticsService.ts";
Expand Down Expand Up @@ -146,6 +147,7 @@ const layerTest = GuestShareService.layer.pipe(
Layer.provide(layerConnect),
Layer.provideMerge(layerAnalytics),
Layer.provide(ServerSecretStore.layer),
Layer.provide(DocumentText.layer),
Layer.provideMerge(layerConfig),
Layer.provideMerge(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer)),
);
Expand Down Expand Up @@ -729,6 +731,11 @@ it.effect("refuses too many files, too many bytes, and images that are not what
contentType: "text/html",
bytes: new TextEncoder().encode("<b>"),
},
{
name: "notes.rtf",
contentType: "application/rtf",
bytes: new TextEncoder().encode(String.raw`{\rtf1 Available}`),
},
]),
);
const [message] = yield* waitingMessages;
Expand All @@ -742,26 +749,46 @@ it.effect("refuses too many files, too many bytes, and images that are not what
[
{ type: "image", name: "screen.png", mimeType: "image/png", sizeBytes: PNG.length },
{ type: "file", name: "notes_fdp.html", mimeType: "text/html", sizeBytes: 3 },
{ type: "file", name: "notes.rtf", mimeType: "application/rtf", sizeBytes: 17 },
],
);
// Agents read a guest's documents through the same extracted text as the owner's uploads.
const { attachmentsDir } = yield* ServerConfig.ServerConfig;
const document = resolveAttachmentPath({
attachmentsDir,
attachment: message!.attachments![2]!,
});
assert.equal(
yield* (yield* FileSystem.FileSystem).readFileString(`${document}.txt`),
"Available",
);
}).pipe(Effect.scoped, Effect.provide(layerTest)),
);

it.effect("caps the bytes of files waiting in one share", () =>
Effect.gen(function* () {
const { guestShares, session, shareId } = yield* joinedGuest;
const submit = (sizeBytes: number) =>
const submit = (sizeBytes: number, ...more: Parameters<typeof uploadsOf>[0]) =>
Effect.gen(function* () {
return guestShares.submit(
session,
{ shareId, text: "Look" },
yield* uploadsOf([png("big.png", sizeBytes)]),
yield* uploadsOf([png("big.png", sizeBytes), ...more]),
);
}).pipe(Effect.flatten);
yield* submit(9 * MIB);
yield* submit(9 * MIB);
const error = yield* Effect.flip(submit(9 * MIB));
const before = yield* storedFiles;
const error = yield* Effect.flip(
submit(9 * MIB, {
name: "notes.rtf",
contentType: "application/rtf",
bytes: new TextEncoder().encode(String.raw`{\rtf1 Available}`),
}),
);
assert.equal(error.reason, "files_busy");
// The refused message keeps neither its files nor their extracted text.
assert.deepEqual(yield* storedFiles, before);
// Text alone still goes through.
yield* guestShares.submit(session, { shareId, text: "One more thing" }, []);
assert.equal((yield* waitingMessages).length, 3);
Expand Down
10 changes: 9 additions & 1 deletion apps/server/src/guest/GuestShareService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
projectTurnItemForDetail,
projectTurnItemForWire,
} from "../orchestration-v2/WireProjection.ts";
import * as DocumentText from "../provider/DocumentText.ts";
import * as ProviderRegistry from "../provider/ProviderRegistry.ts";
import * as ServerSettings from "../serverSettings.ts";
import * as AnalyticsService from "../telemetry/AnalyticsService.ts";
Expand Down Expand Up @@ -227,6 +228,7 @@ const make = Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const random = yield* SecureRandom.SecureRandom;
const analytics = yield* AnalyticsService.AnalyticsService;
const documents = yield* DocumentText.DocumentText;
const signer = yield* PayloadSigner.make(SIGNING_SECRET_NAME);
/** A truncated HMAC keeps invite links short. 128 bits is still far beyond guessing range. */
const inviteSignature = (shareId: string) =>
Expand Down Expand Up @@ -393,7 +395,12 @@ const make = Effect.gen(function* () {
const path = resolveAttachmentPath({ attachmentsDir, attachment });
return path === null
? Effect.void
: fileSystem.remove(path, { force: true }).pipe(Effect.ignore);
: fileSystem
.remove(path, { force: true })
.pipe(
Effect.andThen(fileSystem.remove(`${path}.txt`, { force: true })),
Effect.ignore,
);
},
{ discard: true },
);
Expand Down Expand Up @@ -439,6 +446,7 @@ const make = Effect.gen(function* () {
yield* fileSystem.makeDirectory(attachmentsDir, { recursive: true });
yield* fileSystem.copyFile(uploads[index]!.path, path);
stored.push(attachment);
yield* documents.ensure(path);
Comment thread
saphid marked this conversation as resolved.
Resolved
}).pipe(Effect.mapError(() => uploadFailed)),
{ discard: true },
).pipe(Effect.onError(() => removeStored(stored)));
Expand Down
Loading