Fix MM-61710 (#29248)
* 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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
99f242527e
Коммит
25ff7a3779
@@ -9,6 +9,7 @@ import type {UserProfile} from '@mattermost/types/users';
|
|||||||
|
|
||||||
import {Posts} from 'mattermost-redux/constants';
|
import {Posts} from 'mattermost-redux/constants';
|
||||||
import type {MessageData} from 'mattermost-redux/utils/post_list';
|
import type {MessageData} from 'mattermost-redux/utils/post_list';
|
||||||
|
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
|
||||||
|
|
||||||
import Markdown from 'components/markdown';
|
import Markdown from 'components/markdown';
|
||||||
|
|
||||||
@@ -264,7 +265,8 @@ export class CombinedSystemMessage extends React.PureComponent<Props> {
|
|||||||
return userId !== currentUserId && userId !== currentUsername;
|
return userId !== currentUserId && userId !== currentUsername;
|
||||||
}).
|
}).
|
||||||
map((userId) => {
|
map((userId) => {
|
||||||
return allUsernames[userId] ? `@${allUsernames[userId]}` : someone;
|
const username = secureGetFromRecord(allUsernames, userId);
|
||||||
|
return username ? `@${username}` : someone;
|
||||||
}).
|
}).
|
||||||
filter((username) => {
|
filter((username) => {
|
||||||
return username && username !== '';
|
return username && username !== '';
|
||||||
@@ -299,11 +301,16 @@ export class CombinedSystemMessage extends React.PureComponent<Props> {
|
|||||||
singleline: true,
|
singleline: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectedPostTypeMessage = secureGetFromRecord(postTypeMessage, postType);
|
||||||
|
if (!selectedPostTypeMessage) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
if (numOthers > 1) {
|
if (numOthers > 1) {
|
||||||
return (
|
return (
|
||||||
<LastUsers
|
<LastUsers
|
||||||
actor={actor}
|
actor={actor}
|
||||||
expandedLocale={postTypeMessage[postType].many_expanded}
|
expandedLocale={selectedPostTypeMessage.many_expanded}
|
||||||
formatOptions={options}
|
formatOptions={options}
|
||||||
postType={postType}
|
postType={postType}
|
||||||
usernames={usernames}
|
usernames={usernames}
|
||||||
@@ -313,16 +320,16 @@ export class CombinedSystemMessage extends React.PureComponent<Props> {
|
|||||||
|
|
||||||
let localeHolder: MessageDescriptor = {};
|
let localeHolder: MessageDescriptor = {};
|
||||||
if (numOthers === 0) {
|
if (numOthers === 0) {
|
||||||
localeHolder = postTypeMessage[postType].one;
|
localeHolder = selectedPostTypeMessage.one;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(userIds[0] === this.props.currentUserId || userIds[0] === this.props.currentUsername) &&
|
(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) {
|
} else if (numOthers === 1) {
|
||||||
localeHolder = postTypeMessage[postType].two;
|
localeHolder = selectedPostTypeMessage.two;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedMessage = formatMessage(localeHolder, {firstUser, secondUser, actor});
|
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 type {IntlShape, MessageDescriptor} from 'react-intl';
|
||||||
|
|
||||||
import {Posts} from 'mattermost-redux/constants';
|
import {Posts} from 'mattermost-redux/constants';
|
||||||
|
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
|
||||||
|
|
||||||
import Markdown from 'components/markdown';
|
import Markdown from 'components/markdown';
|
||||||
|
|
||||||
@@ -121,10 +122,12 @@ export class LastUsers extends React.PureComponent<Props, State> {
|
|||||||
{numOthers: lastIndex},
|
{numOthers: lastIndex},
|
||||||
);
|
);
|
||||||
|
|
||||||
const actorMessage = formatMessage(
|
const selectedTypeMessage = secureGetFromRecord(typeMessage, postType);
|
||||||
{id: typeMessage[postType].id, defaultMessage: typeMessage[postType].defaultMessage},
|
|
||||||
|
const actorMessage = selectedTypeMessage ? formatMessage(
|
||||||
|
{id: selectedTypeMessage.id, defaultMessage: selectedTypeMessage.defaultMessage},
|
||||||
{actor},
|
{actor},
|
||||||
);
|
) : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import styled, {css} from 'styled-components';
|
|||||||
import type {PostAction, PostActionOption} from '@mattermost/types/integration_actions';
|
import type {PostAction, PostActionOption} from '@mattermost/types/integration_actions';
|
||||||
|
|
||||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
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 {changeOpacity} from 'mattermost-redux/utils/theme_utils';
|
||||||
|
|
||||||
import Markdown from 'components/markdown';
|
import Markdown from 'components/markdown';
|
||||||
@@ -51,8 +52,8 @@ const ActionButton = ({
|
|||||||
if (action.style) {
|
if (action.style) {
|
||||||
const STATUS_COLORS = getStatusColors(theme);
|
const STATUS_COLORS = getStatusColors(theme);
|
||||||
hexColor =
|
hexColor =
|
||||||
STATUS_COLORS[action.style] ||
|
secureGetFromRecord(STATUS_COLORS, action.style) ||
|
||||||
theme[action.style] ||
|
secureGetFromRecord(theme, action.style) ||
|
||||||
(action.style.match('^#(?:[0-9a-fA-F]{3}){1,2}$') && 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 type {PostAction} from '@mattermost/types/integration_actions';
|
||||||
|
|
||||||
|
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
|
||||||
|
|
||||||
import {autocompleteChannels} from 'actions/channel_actions';
|
import {autocompleteChannels} from 'actions/channel_actions';
|
||||||
import {autocompleteUsers} from 'actions/user_actions';
|
import {autocompleteUsers} from 'actions/user_actions';
|
||||||
import {selectAttachmentMenuAction} from 'actions/views/posts';
|
import {selectAttachmentMenuAction} from 'actions/views/posts';
|
||||||
@@ -22,7 +24,7 @@ export type OwnProps = {
|
|||||||
|
|
||||||
function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
||||||
const actions = state.views.posts.menuActions[ownProps.postId];
|
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 {
|
return {
|
||||||
selected,
|
selected,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type {
|
|||||||
import type {PostImage} from '@mattermost/types/posts';
|
import type {PostImage} from '@mattermost/types/posts';
|
||||||
|
|
||||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||||
|
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
|
||||||
|
|
||||||
import {trackEvent} from 'actions/telemetry_actions';
|
import {trackEvent} from 'actions/telemetry_actions';
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
|
|||||||
if (!attachment.thumb_url) {
|
if (!attachment.thumb_url) {
|
||||||
return;
|
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);
|
this.handleHeightReceived(height);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -111,7 +112,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
|
|||||||
if (!attachment.image_url) {
|
if (!attachment.image_url) {
|
||||||
return;
|
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);
|
this.handleHeightReceived(height);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -369,7 +370,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
|
|||||||
<ExternalImage
|
<ExternalImage
|
||||||
key={'attachment__author-icon'}
|
key={'attachment__author-icon'}
|
||||||
src={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) => (
|
{(iconUrl) => (
|
||||||
<img
|
<img
|
||||||
@@ -459,7 +460,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
|
|||||||
|
|
||||||
let image;
|
let image;
|
||||||
if (attachment.image_url) {
|
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 = (
|
image = (
|
||||||
<div className='attachment__image-container'>
|
<div className='attachment__image-container'>
|
||||||
@@ -485,7 +486,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
|
|||||||
if (attachment.footer) {
|
if (attachment.footer) {
|
||||||
let footerIcon;
|
let footerIcon;
|
||||||
if (attachment.footer_icon) {
|
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 = (
|
footerIcon = (
|
||||||
<ExternalImage
|
<ExternalImage
|
||||||
@@ -515,7 +516,7 @@ export default class MessageAttachment extends React.PureComponent<Props, State>
|
|||||||
|
|
||||||
let thumb;
|
let thumb;
|
||||||
if (attachment.thumb_url) {
|
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 = (
|
thumb = (
|
||||||
<div className='attachment__thumb-container'>
|
<div className='attachment__thumb-container'>
|
||||||
|
|||||||
@@ -1099,12 +1099,14 @@ export function getNeededAtMentionedUsernamesAndGroups(state: GlobalState, posts
|
|||||||
|
|
||||||
if (attachment.fields) {
|
if (attachment.fields) {
|
||||||
for (const field of attachment.fields) {
|
for (const field of attachment.fields) {
|
||||||
|
if (typeof field.value === 'string') {
|
||||||
findNeededUsernamesAndGroups(field.value);
|
findNeededUsernamesAndGroups(field.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return usernamesAndGroupsToLoad;
|
return usernamesAndGroupsToLoad;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
} from 'mattermost-redux/selectors/entities/common';
|
} from 'mattermost-redux/selectors/entities/common';
|
||||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||||
import {getDirectShowPreferences, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
|
import {getDirectShowPreferences, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
|
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
|
||||||
import {
|
import {
|
||||||
displayUsername,
|
displayUsername,
|
||||||
filterProfilesStartingWithTerm,
|
filterProfilesStartingWithTerm,
|
||||||
@@ -687,7 +688,7 @@ export function makeGetProfilesByIdsAndUsernames(): (
|
|||||||
|
|
||||||
if (allUserIds && allUserIds.length > 0) {
|
if (allUserIds && allUserIds.length > 0) {
|
||||||
const profilesById = allUserIds.
|
const profilesById = allUserIds.
|
||||||
filter((userId) => allProfilesById[userId]).
|
filter((userId) => secureGetFromRecord(allProfilesById, userId)).
|
||||||
map((userId) => allProfilesById[userId]);
|
map((userId) => allProfilesById[userId]);
|
||||||
|
|
||||||
if (profilesById && profilesById.length > 0) {
|
if (profilesById && profilesById.length > 0) {
|
||||||
@@ -697,7 +698,7 @@ export function makeGetProfilesByIdsAndUsernames(): (
|
|||||||
|
|
||||||
if (allUsernames && allUsernames.length > 0) {
|
if (allUsernames && allUsernames.length > 0) {
|
||||||
const profilesByUsername = allUsernames.
|
const profilesByUsername = allUsernames.
|
||||||
filter((username) => allProfilesByUsername[username]).
|
filter((username) => secureGetFromRecord(allProfilesByUsername, username)).
|
||||||
map((username) => allProfilesByUsername[username]);
|
map((username) => allProfilesByUsername[username]);
|
||||||
|
|
||||||
if (profilesByUsername && profilesByUsername.length > 0) {
|
if (profilesByUsername && profilesByUsername.length > 0) {
|
||||||
|
|||||||
@@ -249,3 +249,7 @@ export function ensureString(v: unknown) {
|
|||||||
export function ensureNumber(v: unknown) {
|
export function ensureNumber(v: unknown) {
|
||||||
return typeof v === 'number' ? v : 0;
|
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 {TestHelper} from 'utils/test_helper';
|
||||||
import {addTimeToTimestamp, TimeInformation} from 'utils/utils';
|
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/general');
|
||||||
jest.mock('mattermost-redux/selectors/entities/preferences');
|
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 {createSelector} from 'mattermost-redux/selectors/create_selector';
|
||||||
import {isPlugin} from 'mattermost-redux/utils/marketplace';
|
import {isPlugin} from 'mattermost-redux/utils/marketplace';
|
||||||
|
import {secureGetFromRecord} from 'mattermost-redux/utils/post_utils';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
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 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);
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user