diff --git a/packages/client/src/components/media-list.vue b/packages/client/src/components/media-list.vue
index 2970d06c97..efcbb12922 100644
--- a/packages/client/src/components/media-list.vue
+++ b/packages/client/src/components/media-list.vue
@@ -3,7 +3,7 @@
-
+
@@ -22,6 +22,7 @@ import XBanner from './media-banner.vue';
import XImage from './media-image.vue';
import XVideo from './media-video.vue';
import * as os from '@/os';
+import { FILE_TYPE_BROWSERSAFE } from '@/const';
import { defaultStore } from '@/store';
export default defineComponent({
@@ -44,18 +45,23 @@ export default defineComponent({
onMounted(() => {
const lightbox = new PhotoSwipeLightbox({
- dataSource: props.mediaList.filter(media => media.type.startsWith('image')).map(media => {
- const item = {
- src: media.url,
- w: media.properties.width,
- h: media.properties.height,
- alt: media.name,
- };
- if (media.properties.orientation != null && media.properties.orientation >= 5) {
- [item.w, item.h] = [item.h, item.w];
- }
- return item;
- }),
+ dataSource: props.mediaList
+ .filter(media => {
+ if (media.type === 'image/svg+xml') return true; // svgのwebpublicはpngなのでtrue
+ return media.type.startsWith('image') && FILE_TYPE_BROWSERSAFE.includes(media.type);
+ })
+ .map(media => {
+ const item = {
+ src: media.url,
+ w: media.properties.width,
+ h: media.properties.height,
+ alt: media.name,
+ };
+ if (media.properties.orientation != null && media.properties.orientation >= 5) {
+ [item.w, item.h] = [item.h, item.w];
+ }
+ return item;
+ }),
gallery: gallery.value,
children: '.image',
thumbSelector: '.image',
@@ -99,7 +105,9 @@ export default defineComponent({
});
const previewable = (file: misskey.entities.DriveFile): boolean => {
- return file.type.startsWith('video') || file.type.startsWith('image');
+ if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
+ // FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
+ return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);
};
return {
diff --git a/packages/client/src/const.ts b/packages/client/src/const.ts
new file mode 100644
index 0000000000..505cf2748e
--- /dev/null
+++ b/packages/client/src/const.ts
@@ -0,0 +1,44 @@
+// ブラウザで直接表示することを許可するファイルの種類のリスト
+// ここに含まれないものは application/octet-stream としてレスポンスされる
+// SVGはXSSを生むので許可しない
+export const FILE_TYPE_BROWSERSAFE = [
+ // Images
+ 'image/png',
+ 'image/gif',
+ 'image/jpeg',
+ 'image/webp',
+ 'image/apng',
+ 'image/bmp',
+ 'image/tiff',
+ 'image/x-icon',
+
+ // OggS
+ 'audio/opus',
+ 'video/ogg',
+ 'audio/ogg',
+ 'application/ogg',
+
+ // ISO/IEC base media file format
+ 'video/quicktime',
+ 'video/mp4',
+ 'audio/mp4',
+ 'video/x-m4v',
+ 'audio/x-m4a',
+ 'video/3gpp',
+ 'video/3gpp2',
+
+ 'video/mpeg',
+ 'audio/mpeg',
+
+ 'video/webm',
+ 'audio/webm',
+
+ 'audio/aac',
+ 'audio/x-flac',
+ 'audio/vnd.wave',
+];
+/*
+https://github.com/sindresorhus/file-type/blob/main/supported.js
+https://github.com/sindresorhus/file-type/blob/main/core.js
+https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
+*/