* Fix 60879

* Address feedback

* Address feedback

* Add tests

(cherry picked from commit 7f21754b78)

Co-authored-by: Daniel Espino García <larkox@gmail.com>
This commit is contained in:
Mattermost Build 2024-12-04 07:02:33 +01:00 committed by GitHub
parent 298666755c
commit 6d7370972d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 57 additions and 17 deletions

View file

@ -51,7 +51,7 @@ const Document = forwardRef<DocumentRef, DocumentProps>(({canDownloadFiles, chil
let exists = false;
try {
path = decodeURIComponent(file.localPath || '');
path = file.localPath || '';
if (path) {
exists = await fileExists(path);
}
@ -103,7 +103,7 @@ const Document = forwardRef<DocumentRef, DocumentProps>(({canDownloadFiles, chil
const openDocument = useCallback(async () => {
if (!didCancel && !preview) {
let path = decodeURIComponent(file.localPath || '');
let path = file.localPath || '';
let exists = false;
if (path) {
exists = await fileExists(path);
@ -116,7 +116,7 @@ const Document = forwardRef<DocumentRef, DocumentProps>(({canDownloadFiles, chil
setPreview(true);
setStatusBarColor('dark-content');
FileViewer.open(path!.replace('file://', ''), {
displayName: decodeURIComponent(file.name),
displayName: file.name,
onDismiss: onDonePreviewingFile,
showOpenWithDialog: true,
showAppsSuggestions: true,

View file

@ -79,7 +79,7 @@ const FileInfo = ({disabled, file, channelName, showDate, onPress}: FileInfoProp
ellipsizeMode='tail'
style={style.fileName}
>
{decodeURIComponent(file.name.trim())}
{file.name.trim()}
</Text>
<View style={style.fileDownloadContainer}>
{channelName &&

View file

@ -30,7 +30,7 @@ import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/image
import {getMarkdownImageSize} from '@utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {secureGetFromRecord} from '@utils/types';
import {normalizeProtocol, tryOpenURL} from '@utils/url';
import {normalizeProtocol, safeDecodeURIComponent, tryOpenURL} from '@utils/url';
import type {GalleryItemType} from '@typings/screens/gallery';
@ -89,8 +89,8 @@ const MarkdownImage = ({
const uri = source.startsWith('/') ? serverUrl + source : source;
const fileInfo = useMemo(() => {
const link = decodeURIComponent(uri);
let filename = parseUrl(link.substr(link.lastIndexOf('/'))).pathname.replace('/', '');
const decodedLink = safeDecodeURIComponent(uri);
let filename = parseUrl(decodedLink.substr(decodedLink.lastIndexOf('/'))).pathname.replace('/', '');
let extension = metadata?.format || filename.split('.').pop();
if (extension === filename) {
const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
@ -105,7 +105,7 @@ const MarkdownImage = ({
has_preview_image: true,
mime_type: lookupMimeType(filename),
post_id: postId,
uri: link,
uri,
width: originalSize.width,
height: originalSize.height,
} as FileInfo;

View file

@ -14,6 +14,7 @@ import {fileToGalleryItem, openGalleryAtIndex} from '@utils/gallery';
import {generateId} from '@utils/general';
import {calculateDimensions, isGifTooLarge} from '@utils/images';
import {secureGetFromRecord} from '@utils/types';
import {safeDecodeURIComponent} from '@utils/url';
import type {GalleryItemType} from '@typings/screens/gallery';
@ -58,8 +59,9 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL,
const getFileInfo = () => {
const height = metadata?.height || 0;
const width = metadata?.width || 0;
const link = decodeURIComponent(getImageSource());
let filename = parseUrl(link.substr(link.lastIndexOf('/'))).pathname.replace('/', '');
const uri = getImageSource();
const decodedLink = safeDecodeURIComponent(uri);
let filename = parseUrl(decodedLink.substring(decodedLink.lastIndexOf('/'))).pathname.replace('/', '');
let extension = filename.split('.').pop();
if (extension === filename) {
@ -74,7 +76,7 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL,
extension,
has_preview_image: true,
post_id: postId,
uri: link,
uri,
width,
height,
};

View file

@ -308,7 +308,7 @@ const BookmarkFile = ({channelId, close, disabled, initialFile, maxFileSize, set
ellipsizeMode='tail'
style={styles.filename}
>
{decodeURIComponent(file.name.trim())}
{file.name.trim()}
</Text>
{info}
</View>

View file

@ -159,13 +159,13 @@ const ChannelBookmarkAddOrEdit = ({
...(bookmark || emptyBookmark),
owner_id: ownerId,
channel_id: channelId,
display_name: decodeURIComponent(f.name),
display_name: f.name,
type: 'file',
file_id: f.id,
};
setBookmarkToSave(b);
setFile(f);
}, [bookmark, channelId, ownerId]);
}, [bookmark, channelId, ownerId, setBookmarkToSave]);
const setBookmarkDisplayName = useCallback((displayName: string) => {
if (bookmark) {

View file

@ -273,7 +273,7 @@ const DownloadWithAction = ({action, item, onDownloadSuccess, setAction, gallery
actionToExecute({
code: 200,
ok: true,
data: {path},
data: {path: path.replace('file://', '')},
});
} else {
if (item.type === 'avatar') {

View file

@ -11,6 +11,7 @@ import Permissions from 'react-native-permissions';
import {dismissBottomSheet} from '@screens/navigation';
import {extractFileInfo, lookupMimeType} from '@utils/file';
import {logWarning} from '@utils/log';
import {safeDecodeURIComponent} from '@utils/url';
import type {IntlShape} from 'react-intl';
@ -128,7 +129,7 @@ export default class FilePickerUtil {
const type = file.type || lookupMimeType(uri);
let fileName = file.fileName;
if (type.includes('video/') && uri) {
fileName = decodeURIComponent(uri.split('\\').pop()?.split('/').pop() || '');
fileName = safeDecodeURIComponent(uri.split('\\').pop()?.split('/').pop() || '');
}
if (uri) {

View file

@ -413,7 +413,7 @@ export async function extractFileInfo(files: Array<Asset | DocumentPickerRespons
} else {
const localPath = file.uri || '';
try {
const fileInfo = await getInfoAsync(decodeURIComponent(localPath), {size: true});
const fileInfo = await getInfoAsync(localPath, {size: true});
if ('size' in fileInfo) {
outFile.size = fileInfo.size || 0;
outFile.name = localPath.substring(localPath.lastIndexOf('/') + 1);

View file

@ -0,0 +1,29 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {safeDecodeURIComponent} from './index';
describe('safeDecodeURIComponent', () => {
test('should decode a valid URI component', () => {
const encoded = 'Hello%20World';
const decoded = safeDecodeURIComponent(encoded);
expect(decoded).toBe('Hello World');
});
test('should return the input if it is not a valid URI component', () => {
const invalidEncoded = '%E0%A4%A';
const result = safeDecodeURIComponent(invalidEncoded);
expect(result).toBe(invalidEncoded);
});
test('should decode a complex URI component', () => {
const encoded = 'Hello%20World%21%20How%20are%20you%3F';
const decoded = safeDecodeURIComponent(encoded);
expect(decoded).toBe('Hello World! How are you?');
});
test('should return empty string if input is empty', () => {
const encoded = '';
const decoded = safeDecodeURIComponent(encoded);
expect(decoded).toBe('');
});
});

View file

@ -252,3 +252,11 @@ export function extractFilenameFromUrl(url: string) {
const uri = urlParse(url);
return uri.pathname.split('/').pop();
}
export function safeDecodeURIComponent(v: string) {
try {
return decodeURIComponent(v);
} catch {
return v;
}
}