mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2026-08-07 07:14:49 +00:00
fix(aliases): verify alias regex in frontend and backend
Co-authored-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
@@ -282,10 +282,17 @@ describe('AliasService', () => {
|
||||
await expect(service.ensureAliasIsAvailable(alias1)).rejects.toThrow(AlreadyInDBError);
|
||||
expectBindings(tracker, 'select', [[alias1.toLowerCase()]]);
|
||||
});
|
||||
it('returns void if alias can be used', async () => {
|
||||
it.each(['note?', 'alias!', 'pizza ', 'te.st', 'test;1'])(
|
||||
"throws ForbiddenIdError for aliases with invalid alias '%s'",
|
||||
async (alias) => {
|
||||
await expect(service.ensureAliasIsAvailable(alias)).rejects.toThrow(ForbiddenIdError);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([alias1, '🍕', 'pizza', 'Pizza', 'pizzä', 'pißßa'])("accepts '%s'", async (alias) => {
|
||||
mockSelect(tracker, [FieldNameAlias.alias], TableAlias, FieldNameAlias.alias, []);
|
||||
await service.ensureAliasIsAvailable(alias1);
|
||||
expectBindings(tracker, 'select', [[alias1.toLowerCase()]]);
|
||||
await service.ensureAliasIsAvailable(alias);
|
||||
expectBindings(tracker, 'select', [[alias.toLowerCase()]]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Alias, FieldNameAlias, TableAlias } from '@hedgedoc/database';
|
||||
import type { Alias } from '@hedgedoc/database';
|
||||
import { FieldNameAlias, TableAlias } from '@hedgedoc/database';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import base32Encode from 'base32-encode';
|
||||
import { Knex } from 'knex';
|
||||
import { InjectConnection } from 'nest-knexjs';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
import noteConfiguration, { NoteConfig } from '../config/note.config';
|
||||
import noteConfiguration from '../config/note.config';
|
||||
import { NoteConfig } from '../config/note.config';
|
||||
import { AliasDto } from '../dtos/alias.dto';
|
||||
import {
|
||||
AlreadyInDBError,
|
||||
@@ -21,8 +23,10 @@ import {
|
||||
} from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { NoteEvent, NoteEventMap } from '../events';
|
||||
import { NoteEventMap } from '../events';
|
||||
import { NoteEvent } from '../events';
|
||||
import type { NoteAliasesDto } from '../dtos/note-aliases.dto';
|
||||
import { ALIAS_REGEX } from '@hedgedoc/commons';
|
||||
|
||||
type AliasAndIsPrimary = Pick<Alias, FieldNameAlias.alias | FieldNameAlias.isPrimary>;
|
||||
|
||||
@@ -243,6 +247,13 @@ export class AliasService {
|
||||
* @throws AlreadyInDBError The requested alias already exists
|
||||
*/
|
||||
async ensureAliasIsAvailable(alias: string, transaction?: Knex): Promise<void> {
|
||||
if (!ALIAS_REGEX.test(alias)) {
|
||||
throw new ForbiddenIdError(
|
||||
`The alias '${alias}' contains invalid characters.`,
|
||||
this.logger.getContext(),
|
||||
'ensureAliasIsAvailable',
|
||||
);
|
||||
}
|
||||
if (this.isAliasForbidden(alias)) {
|
||||
throw new ForbiddenIdError(
|
||||
`The alias '${alias}' is forbidden by the administrator.`,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from '@jest/globals'
|
||||
import { ALIAS_REGEX } from './alias.js'
|
||||
|
||||
describe('alias regex', () => {
|
||||
describe.each([
|
||||
'🍕',
|
||||
'pizza',
|
||||
'Pizza',
|
||||
'pizzä',
|
||||
'pißßa',
|
||||
'PiZzA🍕',
|
||||
'pi-zza',
|
||||
'pi_zza',
|
||||
'🧝♀️',
|
||||
'⚰️',
|
||||
'🏳️🌈',
|
||||
'🧑🧑🧒🧒',
|
||||
'🇩🇪',
|
||||
])('allows', (testAlias) => {
|
||||
it(testAlias, () => {
|
||||
expect(ALIAS_REGEX.test(testAlias)).toBe(true)
|
||||
})
|
||||
})
|
||||
describe.each(['', '\t', ' ', '\u200D', '⌘', '𝑯𝒆𝒍𝒍𝒐 𝑾𝒐𝒓𝒍𝒅'])('forbids', (testAlias) => {
|
||||
it(testAlias, () => {
|
||||
expect(ALIAS_REGEX.test(testAlias)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
export const ALIAS_REGEX = /^[\p{Letter}\p{Mark}\p{Number}_\-\p{RGI_Emoji}]+$/v
|
||||
@@ -3,5 +3,5 @@
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export * from './alias.js'
|
||||
export * from './username.js'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"extends" : "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"target": "ES2015",
|
||||
"target": "ES2024",
|
||||
"outDir": "dist/cjs",
|
||||
"declarationDir": "dist/cjs",
|
||||
"moduleResolution": "node"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ exports[`AliasesAddForm renders the input form 1`] = `
|
||||
class="me-1 mb-1 input-group has-validation"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
class="form-control is-invalid"
|
||||
data-testid="addAliasInput"
|
||||
placeholder="editor.modal.aliases.addAlias"
|
||||
required=""
|
||||
|
||||
+2
-3
@@ -16,8 +16,7 @@ import type { FormEvent } from 'react'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Button, Form, InputGroup } from 'react-bootstrap'
|
||||
import { PlusLg as IconPlus } from 'react-bootstrap-icons'
|
||||
|
||||
const validAliasRegex = /^[a-zA-Z0-9_-]*$/
|
||||
import { ALIAS_REGEX } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Form for adding a new alias to a note.
|
||||
@@ -47,7 +46,7 @@ export const AliasesAddForm: React.FC = () => {
|
||||
const onNewAliasInputChange = useOnInputChange(setNewAlias)
|
||||
|
||||
const newAliasValid = useMemo(() => {
|
||||
return validAliasRegex.test(newAlias)
|
||||
return ALIAS_REGEX.test(newAlias)
|
||||
}, [newAlias])
|
||||
|
||||
const addAliasText = useTranslatedText('editor.modal.aliases.addAlias')
|
||||
|
||||
Reference in New Issue
Block a user