xslt
xslt.cr
Production-grade, memory-safe, and secure-by-default XSLT 1.0 and EXSLT transformation library for Crystal.
Wraps native libxslt and libexslt through low-level C FFI bindings seamlessly integrated with Crystal standard library XML::Document and IO.
Features
- Memory Safety & Zero Double-Frees: Deep-clones Crystal GC-managed
XML::Documenttrees (xmlCopyDoc) when compiling stylesheets, eliminating double-free segfaults betweenxsltFreeStylesheetand Crystal Boehm GC finalizers. - Secure Sandboxing by Default: Implements zero-trust sandboxing via
Xslt::SecurityPolicy, strictly forbidding Local File Inclusion (LFI) viadocument()or<xsl:include>, Server-Side Request Forgery (SSRF) over network URIs, arbitrary file writes via<exsl:document>, and unauthorized directory creation. - Full EXSLT Integration: Out-of-the-box support for math (
math:max,math:min,math:sqrt), sets (set:distinct,set:difference), strings (str:tokenize,str:replace), dates (date:year), dynamic expressions (dyn:evaluate), and result tree fragments (exsl:node-set). - Flexible Parameter Handling: Automatic parameter escaping and quoting via
params:and dynamic XPath expression evaluation viaxpath_params:. - Streaming & Multi-Target Serialization: Transform directly to
XML::Document, formattedString, or stream directly into any CrystalIO(e.g.,IO::Memory,File,HTTP::Server::Response). - Thread/Fiber Concurrency Safe: Compiled stylesheets are re-entrant and can be shared across multiple fibers concurrently without locks or data races.
- Zero Stderr Leakage: C-level callbacks intercept all diagnostics cleanly into typed exceptions (
Xslt::ParseError,Xslt::TransformError,Xslt::SecurityError).
System Requirements
Requires libxslt and libxml2 development headers:
# Fedora / RHEL / CentOS
sudo dnf install libxslt-devel libxml2-devel
# Debian / Ubuntu
sudo apt-get install libxslt1-dev libxml2-dev
# Arch Linux
sudo pacman -S libxslt libxml2
Installation
Add the dependency to your shard.yml:
dependencies:
xslt:
gitlab: renich/xslt
Run shards install.
Usage
Quick String Transformation
require "xslt"
xml = "<greeting><message>Hello, World!</message></greeting>"
xsl = <<-XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<xsl:template match="/greeting">
<result><xsl:value-of select="message"/></result>
</xsl:template>
</xsl:stylesheet>
XSL
output = Xslt.transform_to_string(xml, xsl)
# => "<result>Hello, World!</result>"
Precompiled Stylesheets & Re-use
For repeated transformations, precompile stylesheets with Xslt::Stylesheet for maximum throughput:
sheet = Xslt::Stylesheet.new(Path["templates/report.xsl"])
# Transform multiple documents concurrently across fibers
100.times do |i|
spawn do
xml = fetch_xml_for(i)
result = sheet.transform_to_string(xml)
process(result)
end
end
Transforming into XML::Document
doc = sheet.transform(xml)
puts doc.root.try(&.name)
Streaming Directly to IO
Avoid intermediate heap allocations by streaming directly to files, memory buffers, or sockets:
File.open("output.html", "w") do |file|
sheet.transform(xml, file)
end
Parameters & Dynamic XPath
# String parameters are auto-quoted safely
result = sheet.transform_to_string(
xml,
params: {
"title" => "User's Report & Summary",
"section" => "Overview",
}
)
# XPath parameters evaluate dynamic XPath expressions at runtime
result = sheet.transform_to_string(
xml,
xpath_params: {
"multiplier" => "2 * 5",
"flag" => "true()",
"count_expr" => "count(//item)",
}
)
Security Sandboxing & Granular Whitelisting
By default, all external file/network reads, file writes, and directory creation are strictly denied:
# Denied by default -> raises Xslt::SecurityError
begin
Xslt.transform_to_string(xml, malicious_xsl)
rescue ex : Xslt::SecurityError
puts "Access denied to: #{ex.target}"
end
# Selectively whitelist specific read paths or network hosts
policy = Xslt::SecurityPolicy.sandbox(
allowed_read_paths: ["/var/data/catalog.xml", Path["/var/data/shared"]],
allowed_read_hosts: ["api.example.com"],
)
sheet = Xslt::Stylesheet.new(xsl, security_policy: policy)
result = sheet.transform_to_string(xml, security_policy: policy)
Exception Hierarchy
All exceptions inherit from Xslt::Error:
Xslt::Error: Base exception class carrying structured diagnostic messages from LibXSLT/LibXML.Xslt::ParseError: Raised when parsing source XML or compiling XSLT stylesheets fails.Xslt::TransformError: Raised when runtime template execution or dynamic XPath evaluation fails.Xslt::SecurityError: Raised when an operation violates security policy boundaries (operation,target).
License
MIT License. Copyright (c) 2026 Rénich Bon Ćirić.
xslt
- 0
- 0
- 0
- 0
- 0
- 2 days ago
- October 2, 2026
MIT License
Fri, 02 Oct 2026 02:59:23 GMT