* Fix MM-61710

* Fix test

* Make more secure the secureGetFromRecord

* Revert changes related to redux posts

* Move segureGetFromRecord

* Fix test

* Use hasOwn

* Address feedback

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Daniel Espino García
2024-11-27 17:13:22 +01:00
коммит произвёл GitHub
родитель 99f242527e
Коммит 25ff7a3779
10 изменённых файлов: 53 добавлений и 24 удалений

Просмотреть файл

@@ -9,6 +9,7 @@ import type {UserProfile} from '@mattermost/types/users';
import {Posts} from 'mattermost-redux/constants';
import type {MessageData} from 'mattermost-redux/utils/post_list';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import Markdown from 'components/markdown';
@@ -264,7 +265,8 @@ export class CombinedSystemMessage extends React.PureComponent<Props> {
return userId !== currentUserId && userId !== currentUsername;
}).
map((userId) => {
return allUsernames[userId] ? `@${allUsernames[userId]}` : someone;
const username = secureGetFromRecord(allUsernames, userId);
return username ? `@${username}` : someone;
}).
filter((username) => {
return username && username !== '';
@@ -299,11 +301,16 @@ export class CombinedSystemMessage extends React.PureComponent<Props> {
singleline: true,
};
const selectedPostTypeMessage = secureGetFromRecord(postTypeMessage, postType);
if (!selectedPostTypeMessage) {
return <></>;
}
if (numOthers > 1) {
return (
<LastUsers
actor={actor}
expandedLocale={postTypeMessage[postType].many_expanded}
expandedLocale={selectedPostTypeMessage.many_expanded}
formatOptions={options}
postType={postType}
usernames={usernames}
@@ -313,16 +320,16 @@ export class CombinedSystemMessage extends React.PureComponent<Props> {
let localeHolder: MessageDescriptor = {};
if (numOthers === 0) {
localeHolder = postTypeMessage[postType].one;
localeHolder = selectedPostTypeMessage.one;
if (
(userIds[0] === this.props.currentUserId || userIds[0] === this.props.currentUsername) &&
postTypeMessage[postType].one_you
selectedPostTypeMessage.one_you
) {
localeHolder = postTypeMessage[postType].one_you;
localeHolder = selectedPostTypeMessage.one_you;
}
} else if (numOthers === 1) {
localeHolder = postTypeMessage[postType].two;
localeHolder = selectedPostTypeMessage.two;
}
const formattedMessage = formatMessage(localeHolder, {firstUser, secondUser, actor});

Просмотреть файл

@@ -6,6 +6,7 @@ import {defineMessages, injectIntl} from 'react-intl';
import type {IntlShape, MessageDescriptor} from 'react-intl';
import {Posts} from 'mattermost-redux/constants';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import Markdown from 'components/markdown';
@@ -121,10 +122,12 @@ export class LastUsers extends React.PureComponent<Props, State> {
{numOthers: lastIndex},
);
const actorMessage = formatMessage(
{id: typeMessage[postType].id, defaultMessage: typeMessage[postType].defaultMessage},
const selectedTypeMessage = secureGetFromRecord(typeMessage, postType);
const actorMessage = selectedTypeMessage ? formatMessage(
{id: selectedTypeMessage.id, defaultMessage: selectedTypeMessage.defaultMessage},
{actor},
);
) : '';
return (
<span>

Просмотреть файл

@@ -7,6 +7,7 @@ import styled, {css} from 'styled-components';
import type {PostAction, PostActionOption} from '@mattermost/types/integration_actions';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import {changeOpacity} from 'mattermost-redux/utils/theme_utils';
import Markdown from 'components/markdown';
@@ -51,8 +52,8 @@ const ActionButton = ({
if (action.style) {
const STATUS_COLORS = getStatusColors(theme);
hexColor =
STATUS_COLORS[action.style] ||
theme[action.style] ||
secureGetFromRecord(STATUS_COLORS, action.style) ||
secureGetFromRecord(theme, action.style) ||
(action.style.match('^#(?:[0-9a-fA-F]{3}){1,2}$') && action.style);
}

Просмотреть файл

@@ -6,6 +6,8 @@ import type {ConnectedProps} from 'react-redux';
import type {PostAction} from '@mattermost/types/integration_actions';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import {autocompleteChannels} from 'actions/channel_actions';
import {autocompleteUsers} from 'actions/user_actions';
import {selectAttachmentMenuAction} from 'actions/views/posts';
@@ -22,7 +24,7 @@ export type OwnProps = {
function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
const actions = state.views.posts.menuActions[ownProps.postId];
const selected = (ownProps.action && ownProps.action.id) ? actions && actions[ownProps.action && ownProps.action.id] : undefined;
const selected = (ownProps.action?.id) ? secureGetFromRecord(actions, ownProps.action.id) : undefined;
return {
selected,

Просмотреть файл

@@ -12,6 +12,7 @@ import type {
import type {PostImage} from '@mattermost/types/posts';
import type {ActionResult} from 'mattermost-redux/types/actions';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import {trackEvent} from 'actions/telemetry_actions';
@@ -101,7 +102,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
if (!attachment.thumb_url) {
return;
}
if (!this.props.imagesMetadata || (this.props.imagesMetadata && !this.props.imagesMetadata[attachment.thumb_url])) {
if (!secureGetFromRecord(this.props.imagesMetadata, attachment.thumb_url)) {
this.handleHeightReceived(height);
}
};
@@ -111,7 +112,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
if (!attachment.image_url) {
return;
}
if (!this.props.imagesMetadata || (this.props.imagesMetadata && !this.props.imagesMetadata[attachment.image_url])) {
if (!secureGetFromRecord(this.props.imagesMetadata, attachment.image_url)) {
this.handleHeightReceived(height);
}
};
@@ -369,7 +370,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
<ExternalImage
key={'attachment__author-icon'}
src={attachment.author_icon}
imageMetadata={this.props.imagesMetadata && this.props.imagesMetadata[attachment.author_icon]}
imageMetadata={secureGetFromRecord(this.props.imagesMetadata, attachment.author_icon)}
>
{(iconUrl) => (
<img
@@ -459,7 +460,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
let image;
if (attachment.image_url) {
const imageMetadata = this.props.imagesMetadata && this.props.imagesMetadata[attachment.image_url];
const imageMetadata = secureGetFromRecord(this.props.imagesMetadata, attachment.image_url);
image = (
<div className='attachment__image-container'>
@@ -485,7 +486,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
if (attachment.footer) {
let footerIcon;
if (attachment.footer_icon) {
const footerIconMetadata = this.props.imagesMetadata && this.props.imagesMetadata[attachment.footer_icon];
const footerIconMetadata = secureGetFromRecord(this.props.imagesMetadata, attachment.footer_icon);
footerIcon = (
<ExternalImage
@@ -515,7 +516,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
let thumb;
if (attachment.thumb_url) {
const thumbMetadata = this.props.imagesMetadata && this.props.imagesMetadata[attachment.thumb_url];
const thumbMetadata = secureGetFromRecord(this.props.imagesMetadata, attachment.thumb_url);
thumb = (
<div className='attachment__thumb-container'>

Просмотреть файл

@@ -1099,7 +1099,9 @@ export function getNeededAtMentionedUsernamesAndGroups(state: GlobalState, posts
if (attachment.fields) {
for (const field of attachment.fields) {
findNeededUsernamesAndGroups(field.value);
if (typeof field.value === 'string') {
findNeededUsernamesAndGroups(field.value);
}
}
}
}

Просмотреть файл

@@ -26,6 +26,7 @@ import {
} from 'mattermost-redux/selectors/entities/common';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {getDirectShowPreferences, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import {
displayUsername,
filterProfilesStartingWithTerm,
@@ -687,7 +688,7 @@ export function makeGetProfilesByIdsAndUsernames(): (
if (allUserIds && allUserIds.length > 0) {
const profilesById = allUserIds.
filter((userId) => allProfilesById[userId]).
filter((userId) => secureGetFromRecord(allProfilesById, userId)).
map((userId) => allProfilesById[userId]);
if (profilesById && profilesById.length > 0) {
@@ -697,7 +698,7 @@ export function makeGetProfilesByIdsAndUsernames(): (
if (allUsernames && allUsernames.length > 0) {
const profilesByUsername = allUsernames.
filter((username) => allProfilesByUsername[username]).
filter((username) => secureGetFromRecord(allProfilesByUsername, username)).
map((username) => allProfilesByUsername[username]);
if (profilesByUsername && profilesByUsername.length > 0) {

Просмотреть файл

@@ -249,3 +249,7 @@ export function ensureString(v: unknown) {
export function ensureNumber(v: unknown) {
return typeof v === 'number' ? v : 0;
}
export function secureGetFromRecord<T>(v: Record<string, T> | undefined, key: string) {
return typeof v === 'object' && v && Object.hasOwn(v, key) ? v[key] : undefined;
}

Просмотреть файл

@@ -14,7 +14,14 @@ import configureStore from 'store';
import {TestHelper} from 'utils/test_helper';
import {addTimeToTimestamp, TimeInformation} from 'utils/utils';
jest.mock('mattermost-redux/selectors/entities/users');
jest.mock('mattermost-redux/selectors/entities/users', () => {
const originalModule = jest.requireActual('mattermost-redux/selectors/entities/users');
return {
...originalModule,
getCurrentUser: jest.fn(),
getUser: jest.fn(),
};
});
jest.mock('mattermost-redux/selectors/entities/general');
jest.mock('mattermost-redux/selectors/entities/preferences');

Просмотреть файл

@@ -5,6 +5,7 @@ import type {MarketplaceApp, MarketplacePlugin} from '@mattermost/types/marketpl
import {createSelector} from 'mattermost-redux/selectors/create_selector';
import {isPlugin} from 'mattermost-redux/utils/marketplace';
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
import type {GlobalState} from 'types/store';
@@ -45,6 +46,6 @@ export const getApp = (state: GlobalState, id: string): MarketplaceApp | undefin
export const getFilter = (state: GlobalState): string => state.views.marketplace.filter;
export const getInstalling = (state: GlobalState, id: string): boolean => Boolean(state.views.marketplace.installing[id]);
export const getInstalling = (state: GlobalState, id: string): boolean => Boolean(secureGetFromRecord(state.views.marketplace.installing, id));
export const getError = (state: GlobalState, id: string): string => state.views.marketplace.errors[id];
export const getError = (state: GlobalState, id: string): string | undefined => secureGetFromRecord(state.views.marketplace.errors, id);