Skip to content

Commit f6fc93b

Browse files
cleanup topic validation (mqttjs#1465)
* clean up Topic Validation * Update src/util/validateTopic.ts Co-authored-by: Vishnu Reddy <vishnureddy17@gmail.com> * update Co-authored-by: Vishnu Reddy <vishnureddy17@gmail.com>
1 parent 6ada284 commit f6fc93b

File tree

2 files changed

+54
-78
lines changed

2 files changed

+54
-78
lines changed

src/util/validateTopic.ts

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,61 @@
1-
import { logger } from './logger.js';
2-
3-
export function validateTopic(topic: string, message: string): boolean {
4-
const end = topic.length - 1;
5-
const endMinus = end - 1;
6-
const slashInPreEnd = endMinus > 0 && topic.charCodeAt(endMinus) !== 47;
1+
/**
2+
* Validate a topic to see if it's valid or not.
3+
* Topics can support Multi-level wildcards (‘#’ U+0023) and single-level wildcards (‘+’ U+002B).
4+
*
5+
* The multi-level wildcard character MUST be specified either on its own or following a topic level separator.
6+
* In either case it MUST be the last character specified in the Topic Filter.
7+
*
8+
* The single-level wildcard character MUST occupy an entire level of the filter.
9+
* For example: "+" is valid, "+/tennis/#" is valid, "sport+" is not valid.
10+
*
11+
* Topics beginning with $: The Server MUST NOT match Topic Filters starting with a wildcard character with Topic Names
12+
*
13+
* Topic names and topic filters must not include the null character (Unicode U+0000)
14+
*
15+
* Topic names and Topic Filters are UTF-8 encoded strings; they must not encode to more than 65,535 bytes.
16+
*
17+
* @param {String} topic - A topic
18+
* @returns {Boolean} If the topic is valid, returns true. Otherwise, returns false.
19+
*/
20+
export function validateTopic(topic: string) {
21+
// Topic must be at least 1 character.
722
if (topic.length === 0) {
8-
// [MQTT-3.8.3-3]
9-
const err = new Error('impossible to ' + message + ' to an empty topic');
10-
logger.info(err);
1123
return false;
1224
}
13-
for (let i = 0; i < topic.length; i++) {
14-
switch (topic.charCodeAt(i)) {
15-
case 35: {
16-
// #
17-
const notAtTheEnd = i !== end;
18-
if (notAtTheEnd || slashInPreEnd) {
19-
const err = new Error('# is only allowed in ' + message + ' in the last position');
20-
logger.info(err);
21-
return false;
22-
}
23-
break;
24-
}
25-
case 43: {
26-
// +
27-
const pastChar = i < end - 1 && topic.charCodeAt(i + 1) !== 47;
28-
const preChar = i > 1 && topic.charCodeAt(i - 1) !== 47;
29-
if (pastChar || preChar) {
30-
const err = new Error('+ is only allowed in ' + message + ' between /');
31-
logger.info(err);
32-
return false;
33-
}
34-
break;
35-
}
25+
const levels: string[] = topic.split('/');
26+
27+
for (const [i, level] of levels.entries()) {
28+
// If SLWC, MUST occupy entire level.
29+
if (level === '+') {
30+
continue;
31+
}
32+
33+
if (level === '#') {
34+
// Validate MLWC at end of topic filter.
35+
return i === levels.length - 1;
36+
}
37+
38+
// Level must not contain more than one MLWC or SLWC character.
39+
if (level.includes('+') || level.includes('#')) {
40+
return false;
3641
}
3742
}
3843
return true;
3944
}
45+
46+
/**
47+
* Validate an array of topics to see if any of them is valid or not
48+
* @param {Array} topics - Array of topics
49+
* @returns {String} If the topics is valid, returns null. Otherwise, returns the invalid one
50+
*/
51+
export function validateTopics(topics: Array<string>) {
52+
if (topics.length === 0) {
53+
return 'empty_topic_list';
54+
}
55+
for (let i = 0; i < topics.length; i++) {
56+
if (!validateTopic(topics[i] as string)) {
57+
return topics[i];
58+
}
59+
}
60+
return null;
61+
}

src/util/validateTopic_2.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)