Skip to content

Commit 9652c45

Browse files
committed
fix: preserve Drive identity checks through Vercel
1 parent 012f554 commit 9652c45

4 files changed

Lines changed: 57 additions & 22 deletions

File tree

apps/media-server/src/__tests__/lib/media-transfer.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,33 @@ function fetcher(
5151
return Object.assign(run, { preconnect: fetch.preconnect }) as typeof fetch;
5252
}
5353
describe("bounded revision downloads", () => {
54+
test("pins the descriptor with an application header and rejects changed identity", async () => {
55+
process.env.MEDIA_SERVER_WEBHOOK_SECRET = "worker-secret";
56+
globalThis.fetch = fetcher((_input, init) => {
57+
const headers = new Headers(init?.headers);
58+
expect(headers.get("if-match")).toBeNull();
59+
expect(headers.get("x-cap-recording-object-identity")).toBe('"identity"');
60+
return Response.json(target());
61+
});
62+
expect(
63+
await getMediaDownloadTarget(
64+
"https://cap.so/api/storage/object",
65+
undefined,
66+
'"identity"',
67+
),
68+
).toEqual(target());
69+
globalThis.fetch = fetcher(() =>
70+
Response.json({ ...target(), objectIdentity: '"changed"' }),
71+
);
72+
await expect(
73+
getMediaDownloadTarget(
74+
"https://cap.so/api/storage/object",
75+
undefined,
76+
'"identity"',
77+
),
78+
).rejects.toThrow("Recording object changed");
79+
});
80+
5481
test("resumes an interrupted pinned revision without rereading completed bytes", async () => {
5582
const path = await destination();
5683
let calls = 0;

apps/media-server/src/lib/media-transfer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ export async function getMediaDownloadTarget(
171171
headers: {
172172
"x-cap-internal-download": "1",
173173
"x-media-server-secret": secret,
174-
...(expectedIdentity ? { "if-match": expectedIdentity } : {}),
174+
...(expectedIdentity
175+
? { "x-cap-recording-object-identity": expectedIdentity }
176+
: {}),
175177
},
176178
signal,
177179
redirect: "manual",

apps/web/__tests__/unit/storage-object-verification.test.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,26 +99,29 @@ describe("recording verification object reads", () => {
9999
expect(mocks.download).not.toHaveBeenCalled();
100100
});
101101

102-
it("authorizes an internal descriptor without downloading media", async () => {
103-
mocks.download.mockReturnValue(
104-
Effect.succeed({
105-
version: 1,
106-
url: "https://www.googleapis.com/drive/v3/files/file/revisions/revision?alt=media",
107-
}),
108-
);
109-
const response = await request({
110-
"x-cap-internal-download": "1",
111-
"x-media-server-secret": "test-media-secret",
112-
"if-match": '"identity"',
113-
});
114-
expect(response.status).toBe(200);
115-
expect(response.headers.get("Cache-Control")).toBe("private, no-store");
116-
expect(mocks.download).toHaveBeenCalledWith(
117-
"owner/video/result.mp4",
118-
expect.objectContaining({ objectIdentity: '"identity"' }),
119-
);
120-
expect(mocks.read).not.toHaveBeenCalled();
121-
});
102+
it.each(["if-match", "x-cap-recording-object-identity"])(
103+
"authorizes an internal descriptor using %s without downloading media",
104+
async (identityHeader) => {
105+
mocks.download.mockReturnValue(
106+
Effect.succeed({
107+
version: 1,
108+
url: "https://www.googleapis.com/drive/v3/files/file/revisions/revision?alt=media",
109+
}),
110+
);
111+
const response = await request({
112+
"x-cap-internal-download": "1",
113+
"x-media-server-secret": "test-media-secret",
114+
[identityHeader]: '"identity"',
115+
});
116+
expect(response.status).toBe(200);
117+
expect(response.headers.get("Cache-Control")).toBe("private, no-store");
118+
expect(mocks.download).toHaveBeenCalledWith(
119+
"owner/video/result.mp4",
120+
expect.objectContaining({ objectIdentity: '"identity"' }),
121+
);
122+
expect(mocks.read).not.toHaveBeenCalled();
123+
},
124+
);
122125

123126
it("does not add metadata requests to ordinary playback", async () => {
124127
const response = await request();

apps/web/app/api/storage/object/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ export async function GET(request: NextRequest) {
186186
if (!("getInternalDownload" in storage))
187187
return new Response("Unsupported storage", { status: 400 });
188188
const target = yield* storage.getInternalDownload(key, {
189-
objectIdentity: request.headers.get("if-match") ?? undefined,
189+
objectIdentity:
190+
request.headers.get("x-cap-recording-object-identity") ??
191+
request.headers.get("if-match") ??
192+
undefined,
190193
signal: request.signal,
191194
});
192195
return Response.json(target, {

0 commit comments

Comments
 (0)