Skip to content

Commit

Permalink
Mustache: Check start tags were matched to an end tag
Browse files Browse the repository at this point in the history
  • Loading branch information
paulharris authored and gittiver committed Dec 2, 2024
1 parent 3e7aae9 commit a8ca7ed
Showing 1 changed file with 59 additions and 16 deletions.
75 changes: 59 additions & 16 deletions include/crow/mustache.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,37 @@ namespace crow // NOTE: Already documented in "crow/app.h"
*/
struct Action
{
bool has_end_match;
char tag_char;
int start;
int end;
int pos;
ActionType t;
Action(ActionType t_, size_t start_, size_t end_, size_t pos_ = 0):
start(static_cast<int>(start_)), end(static_cast<int>(end_)), pos(static_cast<int>(pos_)), t(t_)

Action(char tag_char_, ActionType t_, size_t start_, size_t end_, size_t pos_ = 0):
has_end_match(false), tag_char(tag_char_), start(static_cast<int>(start_)), end(static_cast<int>(end_)), pos(static_cast<int>(pos_)), t(t_)
{
}

bool missing_end_pair() const {
switch (t)
{
case ActionType::Ignore:
case ActionType::Tag:
case ActionType::UnescapeTag:
case ActionType::CloseBlock:
case ActionType::Partial:
return false;

// requires a match
case ActionType::OpenBlock:
case ActionType::ElseBlock:
return !has_end_match;

default:
throw std::logic_error("invalid type");
}
}
};

/**
Expand Down Expand Up @@ -483,7 +506,7 @@ namespace crow // NOTE: Already documented in "crow/app.h"
if (idx == body_.npos)
{
fragments_.emplace_back(static_cast<int>(current), static_cast<int>(body_.size()));
actions_.emplace_back(ActionType::Ignore, 0, 0);
actions_.emplace_back('!', ActionType::Ignore, 0, 0);
break;
}
fragments_.emplace_back(static_cast<int>(current), static_cast<int>(idx));
Expand All @@ -500,7 +523,8 @@ namespace crow // NOTE: Already documented in "crow/app.h"
throw invalid_template_exception("not matched opening tag");
}
current = endIdx + tag_close.size();
switch (body_[idx])
char tag_char = body_[idx];
switch (tag_char)
{
case '#':
idx++;
Expand All @@ -509,7 +533,7 @@ namespace crow // NOTE: Already documented in "crow/app.h"
while (body_[endIdx - 1] == ' ')
endIdx--;
blockPositions.emplace_back(static_cast<int>(actions_.size()));
actions_.emplace_back(ActionType::OpenBlock, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::OpenBlock, idx, endIdx);
break;
case '/':
idx++;
Expand All @@ -522,13 +546,18 @@ namespace crow // NOTE: Already documented in "crow/app.h"
if (body_.compare(idx, endIdx - idx,
body_, matched.start, matched.end - matched.start) != 0)
{
throw invalid_template_exception("not matched {{# {{/ pair: " +
body_.substr(matched.start, matched.end - matched.start) + ", " +
body_.substr(idx, endIdx - idx));
throw invalid_template_exception(
std::string("not matched {{")
+ matched.tag_char
+ "{{/ pair: "
+ body_.substr(matched.start, matched.end - matched.start) + ", "
+ body_.substr(idx, endIdx - idx)
);
}
matched.pos = static_cast<int>(actions_.size());
matched.has_end_match = true;
}
actions_.emplace_back(ActionType::CloseBlock, idx, endIdx, blockPositions.back());
actions_.emplace_back(tag_char, ActionType::CloseBlock, idx, endIdx, blockPositions.back());
blockPositions.pop_back();
break;
case '^':
Expand All @@ -538,19 +567,19 @@ namespace crow // NOTE: Already documented in "crow/app.h"
while (body_[endIdx - 1] == ' ')
endIdx--;
blockPositions.emplace_back(static_cast<int>(actions_.size()));
actions_.emplace_back(ActionType::ElseBlock, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::ElseBlock, idx, endIdx);
break;
case '!':
// do nothing action
actions_.emplace_back(ActionType::Ignore, idx + 1, endIdx);
actions_.emplace_back(tag_char, ActionType::Ignore, idx + 1, endIdx);
break;
case '>': // partial
idx++;
while (body_[idx] == ' ')
idx++;
while (body_[endIdx - 1] == ' ')
endIdx--;
actions_.emplace_back(ActionType::Partial, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::Partial, idx, endIdx);
break;
case '{':
if (tag_open != "{{" || tag_close != "}}")
Expand All @@ -565,7 +594,7 @@ namespace crow // NOTE: Already documented in "crow/app.h"
idx++;
while (body_[endIdx - 1] == ' ')
endIdx--;
actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
current++;
break;
case '&':
Expand All @@ -574,12 +603,12 @@ namespace crow // NOTE: Already documented in "crow/app.h"
idx++;
while (body_[endIdx - 1] == ' ')
endIdx--;
actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
break;
case '=':
// tag itself is no-op
idx++;
actions_.emplace_back(ActionType::Ignore, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::Ignore, idx, endIdx);
endIdx--;
if (body_[endIdx] != '=')
throw invalid_template_exception("{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
Expand Down Expand Up @@ -620,11 +649,25 @@ namespace crow // NOTE: Already documented in "crow/app.h"
idx++;
while (body_[endIdx - 1] == ' ')
endIdx--;
actions_.emplace_back(ActionType::Tag, idx, endIdx);
actions_.emplace_back(tag_char, ActionType::Tag, idx, endIdx);
break;
}
}

// ensure no unmatched tags
for (int i = 0; i < static_cast<int>(actions_.size()); i++)
{
if (actions_[i].missing_end_pair())
{
throw invalid_template_exception(
std::string("open tag has no matching end tag {{")
+ actions_[i].tag_char
+ " {{/ pair: "
+ body_.substr(actions_[i].start, actions_[i].end - actions_[i].start)
);
}
}

// removing standalones
for (int i = static_cast<int>(actions_.size()) - 2; i >= 0; i--)
{
Expand Down

0 comments on commit a8ca7ed

Please sign in to comment.