Skip to content

Commit

Permalink
server+client: add ability to configure allowed file types
Browse files Browse the repository at this point in the history
  • Loading branch information
pbf committed Jul 22, 2023
1 parent 7a82e9d commit 4bf525d
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 38 deletions.
4 changes: 4 additions & 0 deletions client/js/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ class Api extends events.EventTarget {
return remoteConfig.contactEmail;
}

getAllowedExtensions() {
return remoteConfig.allowedExtensions;
}

canSendMails() {
return !!remoteConfig.canSendMails;
}
Expand Down
5 changes: 4 additions & 1 deletion client/js/views/post_upload_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,14 @@ class PostUploadView extends events.EventTarget {
return this._uploadables.findIndex((u2) => u.key === u2.key);
};

let allowedExtensions = api.getAllowedExtensions().map(
function(e) {return "." + e}
);
this._contentFileDropper = new FileDropperControl(
this._contentInputNode,
{
extraText:
"Allowed extensions: .jpg, .png, .gif, .webm, .mp4, .swf, .avif, .heif, .heic",
"Allowed extensions: " + allowedExtensions.join(", "),
allowUrls: true,
allowMultiple: true,
lock: false,
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ version: '2'
services:

server:
image: szurubooru/server:latest
build: server
depends_on:
- sql
environment:
Expand All @@ -27,7 +27,7 @@ services:
- "./server/config.yaml:/opt/app/config.yaml"

client:
image: szurubooru/client:latest
build: client
depends_on:
- server
environment:
Expand Down
12 changes: 12 additions & 0 deletions server/config.yaml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ convert:
to_webm: false
to_mp4: false

# specify which MIME types are allowed
allowed_mime_types:
- image/jpeg
- image/png
- image/gif
- video/webm
- video/mp4
- application/x-shockwave-flash
- image/avif
- image/heif
- image/heic

# allow posts to be uploaded even if some image processing errors occur
allow_broken_uploads: false

Expand Down
7 changes: 6 additions & 1 deletion server/szurubooru/api/info_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Dict, Optional

from szurubooru import config, rest
from szurubooru.func import auth, posts, users, util
from szurubooru.func import auth, mime, posts, users, util

_cache_time = None # type: Optional[datetime]
_cache_result = None # type: Optional[int]
Expand Down Expand Up @@ -49,6 +49,11 @@ def get_info(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Response:
"privileges": util.snake_case_to_lower_camel_case_keys(
config.config["privileges"]
),
"allowedExtensions": [
mime.MIME_EXTENSIONS_MAP[i]
for i in config.config["allowed_mime_types"]
if i in mime.MIME_EXTENSIONS_MAP
],
},
}
if auth.has_privilege(ctx.user, "posts:view:featured"):
Expand Down
64 changes: 31 additions & 33 deletions server/szurubooru/func/mime.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
import re
from collections import ChainMap
from typing import Optional

MIME_TYPES_MAP = {
"image": {
"image/gif": "gif",
"image/jpeg": "jpg",
"image/png": "png",
"image/webp": "webp",
"image/bmp": "bmp",
"image/avif": "avif",
"image/heif": "heif",
"image/heic": "heic",
},
"video": {
"application/ogg": None,
"video/mp4": "mp4",
"video/quicktime": "mov",
"video/webm": "webm",
},
"flash": {
"application/x-shockwave-flash": "swf"
},
"other": {
"application/octet-stream": "dat",
},
}
MIME_EXTENSIONS_MAP = ChainMap(*MIME_TYPES_MAP.values())


def get_mime_type(content: bytes) -> str:
if not content:
Expand Down Expand Up @@ -46,48 +73,19 @@ def get_mime_type(content: bytes) -> str:


def get_extension(mime_type: str) -> Optional[str]:
extension_map = {
"application/x-shockwave-flash": "swf",
"image/gif": "gif",
"image/jpeg": "jpg",
"image/png": "png",
"image/webp": "webp",
"image/bmp": "bmp",
"image/avif": "avif",
"image/heif": "heif",
"image/heic": "heic",
"video/mp4": "mp4",
"video/quicktime": "mov",
"video/webm": "webm",
"application/octet-stream": "dat",
}
return extension_map.get((mime_type or "").strip().lower(), None)
return MIME_EXTENSIONS_MAP.get((mime_type or "").strip().lower(), None)


def is_flash(mime_type: str) -> bool:
return mime_type.lower() == "application/x-shockwave-flash"
return mime_type.lower() in MIME_TYPES_MAP["flash"]


def is_video(mime_type: str) -> bool:
return mime_type.lower() in (
"application/ogg",
"video/mp4",
"video/quicktime",
"video/webm",
)
return mime_type.lower() in MIME_TYPES_MAP["video"]


def is_image(mime_type: str) -> bool:
return mime_type.lower() in (
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/bmp",
"image/avif",
"image/heif",
"image/heic",
)
return mime_type.lower() in MIME_TYPES_MAP["image"]


def is_animated_gif(content: bytes) -> bool:
Expand Down
6 changes: 5 additions & 1 deletion server/szurubooru/func/posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,11 @@ def update_post_content(post: model.Post, content: Optional[bytes]) -> None:

update_signature = False
post.mime_type = mime.get_mime_type(content)
if mime.is_flash(post.mime_type):
if post.mime_type not in config.config["allowed_mime_types"]:
raise InvalidPostContentError(
"File type not allowed: %r" % post.mime_type
)
elif mime.is_flash(post.mime_type):
post.type = model.Post.TYPE_FLASH
elif mime.is_image(post.mime_type):
update_signature = True
Expand Down
2 changes: 2 additions & 0 deletions server/szurubooru/tests/api/test_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def test_info_api(
"smtp": {
"host": "example.com",
},
"allowed_mime_types": ["application/octet-stream"],
}
)
db.session.add_all([post_factory(), post_factory()])
Expand All @@ -54,6 +55,7 @@ def test_info_api(
"posts:view:featured": "regular",
},
"canSendMails": True,
"allowedExtensions": ["dat"],
}

with fake_datetime("2016-01-01 13:00"):
Expand Down
4 changes: 4 additions & 0 deletions server/szurubooru/tests/api/test_post_creating.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ def test_errors_not_spending_ids(
"uploads:use_downloader": model.User.RANK_POWER,
},
"secret": "test",
"allowed_mime_types": [
"image/png",
"image/jpeg",
],
}
)
auth_user = user_factory(rank=model.User.RANK_REGULAR)
Expand Down
48 changes: 48 additions & 0 deletions server/szurubooru/tests/func/test_posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,18 @@ def test_update_post_content_for_new_post(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": [
"image/png",
"image/jpeg",
"image/gif",
"image/bmp",
"image/avif",
"image/heic",
"image/heif",
"video/webm",
"video/mp4",
"application/x-shockwave-flash",
],
}
)
output_file_path = "{}/data/posts/{}".format(tmpdir, output_file_name)
Expand Down Expand Up @@ -526,6 +538,7 @@ def test_update_post_content_to_existing_content(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": ["image/png"],
}
)
post = post_factory()
Expand Down Expand Up @@ -553,6 +566,7 @@ def test_update_post_content_with_broken_content(
},
"secret": "test",
"allow_broken_uploads": allow_broken_uploads,
"allowed_mime_types": ["image/png"],
}
)
post = post_factory()
Expand All @@ -576,13 +590,37 @@ def test_update_post_content_with_invalid_content(
config_injector(
{
"allow_broken_uploads": True,
"allowed_mime_types": ["application/octet-stream"],
}
)
post = model.Post()
with pytest.raises(posts.InvalidPostContentError):
posts.update_post_content(post, input_content)


def test_update_post_content_with_unallowed_mime_type(
tmpdir, config_injector, post_factory, read_asset
):
config_injector(
{
"data_dir": str(tmpdir.mkdir("data")),
"thumbnails": {
"post_width": 300,
"post_height": 300,
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": [],
}
)
post = post_factory()
db.session.add(post)
db.session.flush()
content = read_asset("png.png")
with pytest.raises(posts.InvalidPostContentError):
posts.update_post_content(post, content)


@pytest.mark.parametrize("is_existing", (True, False))
def test_update_post_thumbnail_to_new_one(
tmpdir, config_injector, read_asset, post_factory, is_existing
Expand All @@ -596,6 +634,7 @@ def test_update_post_thumbnail_to_new_one(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": ["image/png"],
}
)
post = post_factory(id=1)
Expand Down Expand Up @@ -637,6 +676,7 @@ def test_update_post_thumbnail_to_default(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": ["image/png"],
}
)
post = post_factory(id=1)
Expand Down Expand Up @@ -677,6 +717,7 @@ def test_update_post_thumbnail_with_broken_thumbnail(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": ["image/png"],
}
)
post = post_factory(id=1)
Expand Down Expand Up @@ -721,6 +762,7 @@ def test_update_post_content_leaving_custom_thumbnail(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": ["image/png"],
}
)
post = post_factory(id=1)
Expand Down Expand Up @@ -754,6 +796,11 @@ def test_update_post_content_convert_heif_to_png_when_processing(
},
"secret": "test",
"allow_broken_uploads": False,
"allowed_mime_types": [
"image/avif",
"image/heic",
"image/heif",
],
}
)
post = post_factory(id=1)
Expand Down Expand Up @@ -1176,6 +1223,7 @@ def test_merge_posts_replaces_content(
"post_height": 300,
},
"secret": "test",
"allowed_mime_types": ["image/png"],
}
)
source_post = post_factory(id=1)
Expand Down

0 comments on commit 4bf525d

Please sign in to comment.