test: D1-5 enforce LiquidTagSubset via restricted parser, reject unsupported tags
This commit is contained in:
@@ -71,7 +71,7 @@ defmodule BDS.MCP.Tools do
|
||||
|
||||
@spec validate_template(String.t()) :: {:ok, %{valid: boolean(), errors: [String.t()]}}
|
||||
def validate_template(source) when is_binary(source) do
|
||||
case Liquex.parse(source) do
|
||||
case Liquex.parse(source, BDS.Rendering.LiquidParser) do
|
||||
{:ok, _ast} ->
|
||||
{:ok, %{valid: true, errors: []}}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ defmodule BDS.Rendering.Filters do
|
||||
end
|
||||
|
||||
defp render_macro_source(template_path, template_source, assigns, context) do
|
||||
with {:ok, template_ast} <- Liquex.parse(template_source),
|
||||
with {:ok, template_ast} <- Liquex.parse(template_source, BDS.Rendering.LiquidParser),
|
||||
{:ok, rendered} <- safe_liquex_render(template_ast, context, assigns) do
|
||||
rendered
|
||||
else
|
||||
|
||||
66
lib/bds/rendering/liquid_parser.ex
Normal file
66
lib/bds/rendering/liquid_parser.ex
Normal file
@@ -0,0 +1,66 @@
|
||||
defmodule BDS.Rendering.LiquidParser do
|
||||
@moduledoc """
|
||||
Restricted Liquid parser enforcing the `LiquidTagSubset` invariant
|
||||
(`specs/template.allium`).
|
||||
|
||||
Only the tags used by the bundled starter templates are recognized:
|
||||
|
||||
* `{% if %}` / `{% elsif %}` / `{% else %}` / `{% endif %}`
|
||||
* `{% for %}` / `{% endfor %}`
|
||||
* `{% assign %}`
|
||||
* `{% render 'partial', name: value %}`
|
||||
* `{{ object }}` output (and whitespace-stripped variants `{%- -%}` / `{{- -}}`)
|
||||
|
||||
Any tag outside this subset (`unless`, `case`, `capture`, `raw`, `comment`,
|
||||
`cycle`, `tablerow`, `increment`, `decrement`, `liquid`, `echo`, `include`)
|
||||
leaves unmatched input and fails the `eos/0` check, producing a parse error.
|
||||
|
||||
Pass this module as the second argument to `Liquex.parse/2` (and
|
||||
`Liquex.parse!/2`) so validation and rendering share the same surface.
|
||||
"""
|
||||
|
||||
import NimbleParsec
|
||||
|
||||
@tags [
|
||||
Liquex.Tag.AssignTag,
|
||||
Liquex.Tag.ForTag,
|
||||
Liquex.Tag.IfTag,
|
||||
Liquex.Tag.RenderTag,
|
||||
Liquex.Tag.ObjectTag
|
||||
]
|
||||
|
||||
tags_parser = Enum.map(@tags, &tag(&1.parse(), {:tag, &1}))
|
||||
|
||||
# Ensure the tags are loaded into scope, otherwise function_exported? will
|
||||
# return false.
|
||||
Enum.each(@tags, &Code.ensure_loaded!/1)
|
||||
|
||||
liquid_tags_parser =
|
||||
@tags
|
||||
|> Enum.filter(&function_exported?(&1, :parse_liquid_tag, 0))
|
||||
|> Enum.map(&tag(&1.parse_liquid_tag(), {:tag, &1}))
|
||||
|> choice()
|
||||
|
||||
# Special case for leading spaces before `{%-` and `{{-`.
|
||||
leading_whitespace =
|
||||
empty()
|
||||
# credo:disable-for-lines:1
|
||||
|> Liquex.Parser.Literal.whitespace(1)
|
||||
|> lookahead(choice([string("{%-"), string("{{-")]))
|
||||
|> ignore()
|
||||
|
||||
base =
|
||||
choice(
|
||||
tags_parser ++
|
||||
[
|
||||
# credo:disable-for-lines:2
|
||||
Liquex.Parser.Literal.text(),
|
||||
leading_whitespace
|
||||
]
|
||||
)
|
||||
|
||||
defcombinatorp(:document, repeat(base))
|
||||
defcombinatorp(:liquid_tag_contents, repeat(liquid_tags_parser))
|
||||
|
||||
defparsec(:parse, parsec(:document) |> eos())
|
||||
end
|
||||
@@ -132,7 +132,7 @@ defmodule BDS.Rendering.TemplateSelection do
|
||||
@spec render_template(String.t(), String.t(), map()) ::
|
||||
{:ok, String.t()} | {:error, String.t()}
|
||||
def render_template(project_id, source, assigns) do
|
||||
with {:ok, template_ast} <- Liquex.parse(source),
|
||||
with {:ok, template_ast} <- Liquex.parse(source, BDS.Rendering.LiquidParser),
|
||||
{:ok, _rendered} = ok <- safe_liquex_render(template_ast, project_id, assigns) do
|
||||
ok
|
||||
else
|
||||
|
||||
@@ -350,7 +350,7 @@ defmodule BDS.Templates do
|
||||
end
|
||||
|
||||
defp validate_liquid(source) do
|
||||
case Liquex.parse(source) do
|
||||
case Liquex.parse(source, BDS.Rendering.LiquidParser) do
|
||||
{:ok, _ast} -> :ok
|
||||
{:error, reason, line} -> {:error, "#{reason} at line #{line}"}
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user