Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potentially invalid emoji code (detect preceding alphanumeric character) #149

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/internal/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ export function notMatch(parser: Parser<unknown>): Parser<null> {
});
}

export function notRegexpBefore<T extends RegExp>(pattern: T): Parser<null> {
return new Parser((input, index, _state) => {
const beforeStr = input.slice(0, index);
return !pattern.test(beforeStr)
? success(index, null)
: failure();
});
}

export const cr = str('\r');
export const lf = str('\n');
export const crlf = str('\r\n');
Expand Down
39 changes: 11 additions & 28 deletions src/internal/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ export const language = P.createLanguage<TypeTable>({
italicAsta: () => {
const mark = P.str('*');
const parser = P.seq(
P.notRegexpBefore(/[a-z0-9]$/i),
mark,
P.alt([alphaAndNum, space]).many(1),
mark,
Expand All @@ -401,18 +402,14 @@ export const language = P.createLanguage<TypeTable>({
if (!result.success) {
return P.failure();
}
// check before
const beforeStr = input.slice(0, index);
if (/[a-z0-9]$/i.test(beforeStr)) {
return P.failure();
}
return P.success(result.index, M.ITALIC(mergeText(result.value[1])));
return P.success(result.index, M.ITALIC(mergeText(result.value[2])));
});
},

italicUnder: () => {
const mark = P.str('_');
const parser = P.seq(
P.notRegexpBefore(/[a-z0-9]$/i),
mark,
P.alt([alphaAndNum, space]).many(1),
mark,
Expand All @@ -422,12 +419,7 @@ export const language = P.createLanguage<TypeTable>({
if (!result.success) {
return P.failure();
}
// check before
const beforeStr = input.slice(0, index);
if (/[a-z0-9]$/i.test(beforeStr)) {
return P.failure();
}
return P.success(result.index, M.ITALIC(mergeText(result.value[1])));
return P.success(result.index, M.ITALIC(mergeText(result.value[2])));
});
},

Expand Down Expand Up @@ -551,6 +543,7 @@ export const language = P.createLanguage<TypeTable>({
mention: () => {
const parser = P.seq(
notLinkLabel,
P.notRegexpBefore(/[a-z0-9]$/i),
P.str('@'),
P.regexp(/[a-z0-9_-]+/i),
P.seq(
Expand All @@ -564,15 +557,10 @@ export const language = P.createLanguage<TypeTable>({
if (!result.success) {
return P.failure();
}
// check before (not mention)
const beforeStr = input.slice(0, index);
if (/[a-z0-9]$/i.test(beforeStr)) {
return P.failure();
}
let invalidMention = false;
const resultIndex = result.index;
const username: string = result.value[2];
const hostname: string | null = result.value[3];
const username: string = result.value[3];
const hostname: string | null = result.value[4];
// remove [.-] of tail of hostname
let modifiedHost = hostname;
if (hostname != null) {
Expand Down Expand Up @@ -637,19 +625,15 @@ export const language = P.createLanguage<TypeTable>({
]));
const parser = P.seq(
notLinkLabel,
P.notRegexpBefore(/[a-z0-9]$/i),
mark,
innerItem.many(1).text(),
).select(2);
).select(3);
return new P.Parser((input, index, state) => {
const result = parser.handler(input, index, state);
if (!result.success) {
return P.failure();
}
// check before
const beforeStr = input.slice(0, index);
if (/[a-z0-9]$/i.test(beforeStr)) {
return P.failure();
}
const resultIndex = result.index;
const resultValue = result.value;
// disallow number only
Expand All @@ -661,14 +645,13 @@ export const language = P.createLanguage<TypeTable>({
},

emojiCode: () => {
const side = P.notMatch(P.regexp(/[a-z0-9]/i));
const mark = P.str(':');
return P.seq(
P.alt([P.lineBegin, side]),
P.notRegexpBefore(/[a-z0-9]$/i),
mark,
P.regexp(/[a-z0-9_+-]+/i),
mark,
P.alt([P.lineEnd, side]),
P.alt([P.lineEnd, P.notMatch(P.regexp(/[a-z0-9]/i))]),
).select(2).map(name => M.EMOJI_CODE(name));
},

Expand Down
1 change: 0 additions & 1 deletion test/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ after`;
test('nested', () => {
const nodes = mfm.parse('abc:hoge:$[tada 123 @hoge :foo:]:piyo:');
const expect = [
EMOJI_CODE('hoge'),
EMOJI_CODE('foo'),
EMOJI_CODE('piyo')
];
Expand Down
6 changes: 6 additions & 0 deletions test/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ describe('SimpleParser', () => {
const output = [TEXT('あ'), EMOJI_CODE('bar'), TEXT('い')];
assert.deepStrictEqual(mfm.parseSimple(input), output);
});

test('end of text', () => {
const input = 'At 10:30:';
const output = [TEXT('At 10:30:')];
assert.deepStrictEqual(mfm.parseSimple(input), output);
});
});

test('disallow other syntaxes', () => {
Expand Down