Enforce Liquid tag/filter/operator subset at template publish #67

Closed
opened 2026-07-20 19:43:56 +00:00 by hugo · 1 comment
Owner

Source: Audit section 1.3 (high impact).

Spec: specs/template.allium invariants LiquidTagSubset / LiquidFilterSubset / LiquidOperatorSubset explicitly state the subset is "Enforced at publish/validation time; any other filter/operator is rejected even though the engine would otherwise apply it." Allowed: tags if/elsif/else/endif, for, assign, render (named params), whitespace variants; filters escape, url_encode, default, append + custom i18n, markdown, slugify; operators ==, >, and, or, bare truthiness, blank; dot/.size/bracket access.

bDS2 behaviour: ../bDS2/lib/bds/rendering/liquid_parser.ex returns e.g. {:error, "unsupported filter: upcase"}; publish_template refuses and the template stays draft. Tests: ../bDS2/test/bds/templates_test.exs (~lines 330-400) cover rejected tags, rejected filters (upcase, downcase, date, truncate, split, reverse), allowed filters (escape, url_encode, slugify), rejected operators (!=, <, >=, <=, contains).

Current behaviour (RuDS): crates/bds-core/src/engine/template.rs fn validate_template (line 138) only checks {{ }}/{% %} bracket balance and if/for block matching (validate_liquid_brackets, validate_liquid_blocks). Unsupported constructs publish fine.

Task: Extend validate_template to parse tags, filters, and comparison operators and reject anything outside the subset with bDS2-compatible messages ("unsupported filter: {name}", "unsupported tag: {name}", "unsupported operator: {op}"). Port the bDS2 test matrix to Rust tests. The same validation runs for bds.templates.validate in the scripting host and MCP propose_template — verify those paths use validate_template.

Acceptance: All ported bDS2 cases pass; publishing a template with {{ title | upcase }} fails and leaves it draft.

**Source:** Audit section 1.3 (high impact). **Spec:** `specs/template.allium` invariants LiquidTagSubset / LiquidFilterSubset / LiquidOperatorSubset explicitly state the subset is "Enforced at publish/validation time; any other filter/operator is rejected even though the engine would otherwise apply it." Allowed: tags if/elsif/else/endif, for, assign, render (named params), whitespace variants; filters escape, url_encode, default, append + custom i18n, markdown, slugify; operators ==, >, and, or, bare truthiness, blank; dot/.size/bracket access. **bDS2 behaviour:** `../bDS2/lib/bds/rendering/liquid_parser.ex` returns e.g. `{:error, "unsupported filter: upcase"}`; `publish_template` refuses and the template stays draft. Tests: `../bDS2/test/bds/templates_test.exs` (~lines 330-400) cover rejected tags, rejected filters (upcase, downcase, date, truncate, split, reverse), allowed filters (escape, url_encode, slugify), rejected operators (!=, <, >=, <=, contains). **Current behaviour (RuDS):** `crates/bds-core/src/engine/template.rs` fn `validate_template` (line 138) only checks `{{ }}`/`{% %}` bracket balance and if/for block matching (`validate_liquid_brackets`, `validate_liquid_blocks`). Unsupported constructs publish fine. **Task:** Extend `validate_template` to parse tags, filters, and comparison operators and reject anything outside the subset with bDS2-compatible messages ("unsupported filter: {name}", "unsupported tag: {name}", "unsupported operator: {op}"). Port the bDS2 test matrix to Rust tests. The same validation runs for `bds.templates.validate` in the scripting host and MCP propose_template — verify those paths use `validate_template`. **Acceptance:** All ported bDS2 cases pass; publishing a template with `{{ title | upcase }}` fails and leaves it draft.
hugo added the bug label 2026-07-20 19:43:56 +00:00
Author
Owner

Implemented in 3ac7a8b and pushed to origin/main. Template validation now enforces the exact Allium/bDS2 Liquid surface: if/elsif/else, for, assign, render with named parameters, whitespace-control variants; escape, url_encode, default, append, i18n, markdown, slugify; and only ==/> comparisons with and/or, truthiness, blank, dot/.size, and bracket access. Unsupported constructs return the bDS2-compatible messages unsupported tag/filter/operator: name. Validation now also runs through the same real Liquid parser configuration used by rendering. Publish failure leaves the template draft, preserves DB content, and writes no file. Tests port the bDS2 rejection/allowance matrix, validate all bundled starter templates, verify bds.templates.validate returns the same error, and verify MCP propose_template creates no proposal on rejection. Verification: cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo build --workspace, cargo test --workspace (bds-core 487 passed/1 ignored; bds-ui 170 passed; all integration/doc tests passed), git diff --check, allium check specs, and allium analyse specs. A neutral issue/bDS2/spec/code-path review found no remaining gap.

Implemented in 3ac7a8b and pushed to origin/main. Template validation now enforces the exact Allium/bDS2 Liquid surface: if/elsif/else, for, assign, render with named parameters, whitespace-control variants; escape, url_encode, default, append, i18n, markdown, slugify; and only ==/> comparisons with and/or, truthiness, blank, dot/.size, and bracket access. Unsupported constructs return the bDS2-compatible messages unsupported tag/filter/operator: name. Validation now also runs through the same real Liquid parser configuration used by rendering. Publish failure leaves the template draft, preserves DB content, and writes no file. Tests port the bDS2 rejection/allowance matrix, validate all bundled starter templates, verify bds.templates.validate returns the same error, and verify MCP propose_template creates no proposal on rejection. Verification: cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo build --workspace, cargo test --workspace (bds-core 487 passed/1 ignored; bds-ui 170 passed; all integration/doc tests passed), git diff --check, allium check specs, and allium analyse specs. A neutral issue/bDS2/spec/code-path review found no remaining gap.
hugo closed this issue 2026-07-21 18:21:59 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: hugo/RuDS#67