mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
fix: Non-members cannot see public ACL attachments (#11326)
closes #11324
This commit is contained in:
@@ -340,6 +340,75 @@ describe("#files.get", () => {
|
||||
expect(res.headers.get("Content-Disposition")).toEqual("attachment");
|
||||
});
|
||||
|
||||
it("should succeed with status 200 ok when public-read avatar in uploads bucket is requested by non-owner", async () => {
|
||||
const owner = await buildUser();
|
||||
const otherUser = await buildUser({ teamId: owner.teamId });
|
||||
const key = AttachmentHelper.getKey({
|
||||
id: randomUUID(),
|
||||
name: "avatar.jpg",
|
||||
userId: owner.id,
|
||||
});
|
||||
await buildAttachment({
|
||||
key,
|
||||
teamId: owner.teamId,
|
||||
userId: owner.id,
|
||||
contentType: "image/jpg",
|
||||
acl: "public-read",
|
||||
});
|
||||
|
||||
ensureDirSync(
|
||||
path.dirname(path.join(env.FILE_STORAGE_LOCAL_ROOT_DIR, key))
|
||||
);
|
||||
|
||||
copyFileSync(
|
||||
path.resolve(__dirname, "..", "test", "fixtures", "avatar.jpg"),
|
||||
path.join(env.FILE_STORAGE_LOCAL_ROOT_DIR, key)
|
||||
);
|
||||
|
||||
// Non-owner user should be able to access public-read attachment
|
||||
const res = await server.get(`/api/files.get?key=${key}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${otherUser.getJwtToken()}`,
|
||||
},
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get("Content-Type")).toEqual("image/jpg");
|
||||
});
|
||||
|
||||
it("should fail with status 403 when private attachment in uploads bucket is requested by non-owner", async () => {
|
||||
const owner = await buildUser();
|
||||
const otherUser = await buildUser({ teamId: owner.teamId });
|
||||
const key = AttachmentHelper.getKey({
|
||||
id: randomUUID(),
|
||||
name: "document.pdf",
|
||||
userId: owner.id,
|
||||
});
|
||||
await buildAttachment({
|
||||
key,
|
||||
teamId: owner.teamId,
|
||||
userId: owner.id,
|
||||
contentType: "application/pdf",
|
||||
acl: "private",
|
||||
});
|
||||
|
||||
ensureDirSync(
|
||||
path.dirname(path.join(env.FILE_STORAGE_LOCAL_ROOT_DIR, key))
|
||||
);
|
||||
|
||||
copyFileSync(
|
||||
path.resolve(__dirname, "..", "test", "fixtures", "avatar.jpg"),
|
||||
path.join(env.FILE_STORAGE_LOCAL_ROOT_DIR, key)
|
||||
);
|
||||
|
||||
// Non-owner user should NOT be able to access private attachment
|
||||
const res = await server.get(`/api/files.get?key=${key}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${otherUser.getJwtToken()}`,
|
||||
},
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it("should succeed with status 200 ok when exported file is requested using signature", async () => {
|
||||
const user = await buildUser();
|
||||
const fileName = "export-markdown.zip";
|
||||
|
||||
@@ -77,10 +77,15 @@ router.get(
|
||||
const forceDownload = !!ctx.input.query.download;
|
||||
const isSignedRequest = !!ctx.input.query.sig;
|
||||
const { isPublicBucket, fileName } = AttachmentHelper.parseKey(key);
|
||||
const skipAuthorize = isPublicBucket || isSignedRequest;
|
||||
const cacheHeader = "max-age=604800, immutable";
|
||||
const attachment = await Attachment.findByKey(key);
|
||||
|
||||
// Skip authorization for public bucket, signed requests, or public-read ACL attachments
|
||||
const skipAuthorize =
|
||||
isPublicBucket ||
|
||||
isSignedRequest ||
|
||||
(attachment && !attachment.isPrivate);
|
||||
|
||||
if (!skipAuthorize) {
|
||||
if (!attachment && !!ctx.input.query.key) {
|
||||
throw NotFoundError();
|
||||
|
||||
Reference in New Issue
Block a user