[parsing/mfm] Limit inline node recursion to 100
This commit is contained in:
parent
9036eacd98
commit
aa593f78b8
2 changed files with 88 additions and 36 deletions
|
@ -110,9 +110,13 @@ module MfmNodeTypes =
|
|||
|
||||
type internal UserState =
|
||||
{ ParenthesisStack: char list
|
||||
LastLine: int64 }
|
||||
LastLine: int64
|
||||
Depth: int64 }
|
||||
|
||||
static member Default = { ParenthesisStack = []; LastLine = 0 }
|
||||
static member Default =
|
||||
{ ParenthesisStack = []
|
||||
LastLine = 0
|
||||
Depth = 0 }
|
||||
|
||||
open MfmNodeTypes
|
||||
|
||||
|
@ -220,10 +224,10 @@ module private MfmParser =
|
|||
|
||||
let clearParen = updateUserState <| fun u -> { u with ParenthesisStack = [] }
|
||||
|
||||
let (|GreaterEqualThan|_|) k value = if value >= k then Some() else None
|
||||
|
||||
// References
|
||||
let node, nodeRef = createParserForwardedToRef ()
|
||||
let inlineNode, inlineNodeRef = createParserForwardedToRef ()
|
||||
let simple, simpleRef = createParserForwardedToRef ()
|
||||
|
||||
let seqFlatten items =
|
||||
seq {
|
||||
|
@ -451,6 +455,9 @@ module private MfmParser =
|
|||
|
||||
let prefixedNode (m: ParseMode) : Parser<MfmNode, UserState> =
|
||||
fun (stream: CharStream<_>) ->
|
||||
match stream.UserState.Depth with
|
||||
| GreaterEqualThan 100L -> stream |> charNode
|
||||
| _ ->
|
||||
match (stream.Peek(), m) with
|
||||
// Block nodes, ordered by expected frequency
|
||||
| '`', Full -> codeBlockNode <|> codeNode
|
||||
|
@ -481,13 +488,16 @@ module private MfmParser =
|
|||
attempt <| prefixedNode m <|> charNode
|
||||
|
||||
// Populate references
|
||||
do nodeRef.Value <- parseNode Full
|
||||
do inlineNodeRef.Value <- parseNode Inline |>> fun v -> v :?> MfmInlineNode
|
||||
do simpleRef.Value <- parseNode Simple
|
||||
let pushDepth = updateUserState (fun u -> { u with Depth = (u.Depth + 1L) })
|
||||
let popDepth = updateUserState (fun u -> { u with Depth = (u.Depth - 1L) })
|
||||
do inlineNodeRef.Value <- pushDepth >>. (parseNode Inline |>> fun v -> v :?> MfmInlineNode) .>> popDepth
|
||||
|
||||
// Parser abstractions
|
||||
let node = parseNode Full
|
||||
let simple = parseNode Simple
|
||||
|
||||
// Final parse command
|
||||
let parse = spaces >>. manyTill node eof .>> spaces
|
||||
|
||||
let parseSimple = spaces >>. manyTill simple eof .>> spaces
|
||||
|
||||
open MfmParser
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Text;
|
||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Serialization;
|
||||
using Iceshrimp.Parsing;
|
||||
using Microsoft.FSharp.Collections;
|
||||
using static Iceshrimp.Parsing.MfmNodeTypes;
|
||||
using FSDict = System.Collections.Generic.Dictionary<string, Microsoft.FSharp.Core.FSharpOption<string>?>;
|
||||
|
||||
|
@ -516,10 +518,50 @@ public class MfmTests
|
|||
</center>
|
||||
""";
|
||||
|
||||
AssertionOptions.FormattingOptions.MaxDepth = 100;
|
||||
var res = Mfm.parse(input);
|
||||
MfmSerializer.Serialize(res).Should().BeEquivalentTo(input);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFnRecursionLimit()
|
||||
{
|
||||
const int iterations = 150;
|
||||
const int limit = 100;
|
||||
|
||||
var input = GetMfm(iterations);
|
||||
var result = Mfm.parse(input);
|
||||
|
||||
// Closing brackets will be grouped at the end, since fn node parser isn't greedy
|
||||
List<MfmNode> expected = [GetExpected(iterations), new MfmTextNode(new string(']', iterations - limit))];
|
||||
|
||||
AssertionOptions.FormattingOptions.MaxDepth = 300;
|
||||
AssertionOptions.FormattingOptions.MaxLines = 1000;
|
||||
result.ToList().Should().Equal(expected, MfmNodeEqual);
|
||||
MfmSerializer.Serialize(result).Should().BeEquivalentTo(input);
|
||||
|
||||
return;
|
||||
|
||||
string GetMfm(int count)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < count; i++)
|
||||
sb.Append("$[test ");
|
||||
for (var i = 0; i < count; i++)
|
||||
sb.Append(']');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
MfmInlineNode GetExpected(int count, int remaining = limit)
|
||||
{
|
||||
if (remaining <= 0)
|
||||
return new MfmTextNode(GetMfm(count).TrimEnd(']'));
|
||||
|
||||
return new MfmFnNode("test", null, [GetExpected(--count, --remaining)]);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MfmNodeEqual(MfmNode a, MfmNode b)
|
||||
{
|
||||
if (a.GetType() != b.GetType()) return false;
|
||||
|
|
Loading…
Add table
Reference in a new issue