Sanitize sqlite like queries and allow non-latin characters (#7141)

This commit is contained in:
Elias Nahum
2023-02-16 11:18:05 +02:00
committed by GitHub
parent 78190cbc47
commit 86fff5c728
5 changed files with 61 additions and 9 deletions

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {sanitizeLikeString} from '.';
describe('Test SQLite Sanitize like string with latin and non-latin characters', () => {
const disallowed = ',./;[]!@#$%^&*()_-=+~';
test('test (latin)', () => {
expect(sanitizeLikeString('test123')).toBe('test123');
expect(sanitizeLikeString(`test123${disallowed}`)).toBe(`test123${'_'.repeat(disallowed.length)}`);
});
test('test (arabic)', () => {
expect(sanitizeLikeString('اختبار123')).toBe('اختبار123');
expect(sanitizeLikeString(`اختبار123${disallowed}`)).toBe(`اختبار123${'_'.repeat(disallowed.length)}`);
});
test('test (greek)', () => {
expect(sanitizeLikeString(οκιμή123')).toBe(οκιμή123');
expect(sanitizeLikeString(`δοκιμή123${disallowed}`)).toBe(`δοκιμή123${'_'.repeat(disallowed.length)}`);
});
test('test (hebrew)', () => {
expect(sanitizeLikeString('חשבון123')).toBe('חשבון123');
expect(sanitizeLikeString(`חשבון123${disallowed}`)).toBe(`חשבון123${'_'.repeat(disallowed.length)}`);
});
test('test (russian)', () => {
expect(sanitizeLikeString(ест123')).toBe(ест123');
expect(sanitizeLikeString(`тест123${disallowed}`)).toBe(`тест123${'_'.repeat(disallowed.length)}`);
});
test('test (chinese trad)', () => {
expect(sanitizeLikeString('測試123')).toBe('測試123');
expect(sanitizeLikeString(`測試123${disallowed}`)).toBe(`測試123${'_'.repeat(disallowed.length)}`);
});
test('test (japanese)', () => {
expect(sanitizeLikeString('テスト123')).toBe('テスト123');
expect(sanitizeLikeString(`テスト123${disallowed}`)).toBe(`テスト123${'_'.repeat(disallowed.length)}`);
});
});

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import xRegExp from 'xregexp';
import {General} from '@constants';
import type Model from '@nozbe/watermelondb/Model';
@@ -91,3 +93,7 @@ export const filterAndSortMyChannels = ([myChannels, channels, notifyProps]: Fil
return [...mentions, ...unreads, ...mutedMentions];
};
// Matches letters from any alphabet and numbers
const sqliteLikeStringRegex = xRegExp('[^\\p{L}\\p{Nd}]', 'g');
export const sanitizeLikeString = (value: string) => value.replace(sqliteLikeStringRegex, '_');

View File

@@ -9,6 +9,7 @@ import {map as map$, switchMap, distinctUntilChanged} from 'rxjs/operators';
import {General, Permissions} from '@constants';
import {MM_TABLES} from '@constants/database';
import {sanitizeLikeString} from '@helpers/database';
import {hasPermission} from '@utils/role';
import {prepareDeletePost} from './post';
@@ -523,7 +524,7 @@ export function queryMyRecentChannels(database: Database, take: number) {
export const observeDirectChannelsByTerm = (database: Database, term: string, take = 20, matchStart = false) => {
const onlyDMs = term.startsWith('@') ? "AND c.type='D'" : '';
const value = Q.sanitizeLikeString(term.startsWith('@') ? term.substring(1) : term);
const value = sanitizeLikeString(term.startsWith('@') ? term.substring(1) : term);
let username = `u.username LIKE '${value}%'`;
let displayname = `c.display_name LIKE '${value}%'`;
if (!matchStart) {
@@ -549,7 +550,7 @@ export const observeDirectChannelsByTerm = (database: Database, term: string, ta
export const observeNotDirectChannelsByTerm = (database: Database, term: string, take = 20, matchStart = false) => {
const teammateNameSetting = observeTeammateNameDisplay(database);
const value = Q.sanitizeLikeString(term.startsWith('@') ? term.substring(1) : term);
const value = sanitizeLikeString(term.startsWith('@') ? term.substring(1) : term);
let username = `u.username LIKE '${value}%'`;
let nickname = `u.nickname LIKE '${value}%'`;
let displayname = `(u.first_name || ' ' || u.last_name) LIKE '${value}%'`;
@@ -590,7 +591,7 @@ export const observeJoinedChannelsByTerm = (database: Database, term: string, ta
return of$([]);
}
const value = Q.sanitizeLikeString(term);
const value = sanitizeLikeString(term);
let displayname = `c.display_name LIKE '${value}%'`;
if (!matchStart) {
displayname = `c.display_name LIKE '%${value}%' AND c.display_name NOT LIKE '${value}%'`;
@@ -608,7 +609,7 @@ export const observeArchiveChannelsByTerm = (database: Database, term: string, t
return of$([]);
}
const value = Q.sanitizeLikeString(term);
const value = sanitizeLikeString(term);
const displayname = `%${value}%`;
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.on(CHANNEL, Q.and(
@@ -639,7 +640,7 @@ export const observeChannelsByLastPostAt = (database: Database, myChannels: MyCh
};
export const queryChannelsForAutocomplete = (database: Database, matchTerm: string, isSearch: boolean, teamId: string) => {
const likeTerm = `%${Q.sanitizeLikeString(matchTerm)}%`;
const likeTerm = `%${sanitizeLikeString(matchTerm)}%`;
const clauses: Q.Clause[] = [];
if (isSearch) {
clauses.push(

View File

@@ -4,6 +4,7 @@
import {Database, Q} from '@nozbe/watermelondb';
import {MM_TABLES} from '@constants/database';
import {sanitizeLikeString} from '@helpers/database';
import type GroupModel from '@typings/database/models/servers/group';
import type GroupChannelModel from '@typings/database/models/servers/group_channel';
@@ -14,7 +15,7 @@ const {SERVER: {GROUP, GROUP_CHANNEL, GROUP_MEMBERSHIP, GROUP_TEAM}} = MM_TABLES
export const queryGroupsByName = (database: Database, name: string) => {
return database.collections.get<GroupModel>(GROUP).query(
Q.where('name', Q.like(`%${Q.sanitizeLikeString(name)}%`)),
Q.where('name', Q.like(`%${sanitizeLikeString(name)}%`)),
);
};
@@ -27,14 +28,14 @@ export const queryGroupsByNames = (database: Database, names: string[]) => {
export const queryGroupsByNameInTeam = (database: Database, name: string, teamId: string) => {
return database.collections.get<GroupModel>(GROUP).query(
Q.on(GROUP_TEAM, 'team_id', teamId),
Q.where('name', Q.like(`%${Q.sanitizeLikeString(name)}%`)),
Q.where('name', Q.like(`%${sanitizeLikeString(name)}%`)),
);
};
export const queryGroupsByNameInChannel = (database: Database, name: string, channelId: string) => {
return database.collections.get<GroupModel>(GROUP).query(
Q.on(GROUP_CHANNEL, 'channel_id', channelId),
Q.where('name', Q.like(`%${Q.sanitizeLikeString(name)}%`)),
Q.where('name', Q.like(`%${sanitizeLikeString(name)}%`)),
);
};

View File

@@ -7,6 +7,7 @@ import {distinctUntilChanged, switchMap} from 'rxjs/operators';
import {MM_TABLES} from '@constants/database';
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
import {sanitizeLikeString} from '@helpers/database';
import {queryDisplayNamePreferences} from './preference';
import {observeCurrentUserId, observeLicense, getCurrentUserId, getConfig, getLicense, observeConfigValue} from './system';
@@ -86,7 +87,7 @@ export async function getTeammateNameDisplay(database: Database) {
export const queryUsersLike = (database: Database, likeUsername: string) => {
return database.get<UserModel>(USER).query(
Q.where('username', Q.like(
`%${Q.sanitizeLikeString(likeUsername)}%`,
`%${sanitizeLikeString(likeUsername)}%`,
)),
);
};