feat(elc, runtime): RBrace stop in parse_html_children; html_raw/html_escape; elc.c canonical
El SDK CI - dev / build-and-test (pull_request) Failing after 13m49s
El SDK CI - dev / build-and-test (pull_request) Failing after 13m49s
parse_html_children consumed the closing `}` of the outer El function as HTML text content when a tag was left open across a function boundary (e.g. `page_open()` opens `<body>` without a closing `</body>`). Fix: stop the children loop when the current token is RBrace — that token belongs to the El function, not the HTML tree. Add html_raw() and html_escape() builtins to el_runtime so templates can interpolate trusted raw HTML and safely escape user-supplied content. Rename elc-new.c → elc.c as the canonical compiler source; rebuild elc binary from it.
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -2241,6 +2241,43 @@ el_val_t url_decode(el_val_t sv) {
|
||||
return el_wrap_str(out);
|
||||
}
|
||||
|
||||
/* ── html_raw ────────────────────────────────────────────────────────────────
|
||||
* Identity passthrough for raw HTML template interpolation.
|
||||
* El's {raw(expr)} compiles to html_raw(expr) — the value is output as-is
|
||||
* without any escaping. The caller is responsible for safety.
|
||||
*/
|
||||
el_val_t html_raw(el_val_t s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* ── html_escape ─────────────────────────────────────────────────────────────
|
||||
* Escape < > " ' & for safe HTML text interpolation.
|
||||
* El's {expr} in HTML templates compiles to html_escape(expr).
|
||||
*/
|
||||
el_val_t html_escape(el_val_t sv) {
|
||||
const char* src = EL_CSTR(sv);
|
||||
if (!src) return EL_STR("");
|
||||
size_t len = strlen(src);
|
||||
/* Worst case: every byte → 6 chars (") */
|
||||
char* out = (char*)malloc(len * 6 + 1);
|
||||
if (!out) return sv;
|
||||
el_arena_track(out);
|
||||
char* p = out;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char c = (unsigned char)src[i];
|
||||
switch (c) {
|
||||
case '&': memcpy(p, "&", 5); p += 5; break;
|
||||
case '<': memcpy(p, "<", 4); p += 4; break;
|
||||
case '>': memcpy(p, ">", 4); p += 4; break;
|
||||
case '"': memcpy(p, """, 6); p += 6; break;
|
||||
case '\'': memcpy(p, "'", 5); p += 5; break;
|
||||
default: *p++ = (char)c; break;
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
return el_wrap_str(out);
|
||||
}
|
||||
|
||||
/* ── HTML allowlist sanitizer ────────────────────────────────────────────────
|
||||
* el_html_sanitize(input, allowlist_json)
|
||||
*
|
||||
|
||||
@@ -227,6 +227,8 @@ el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */
|
||||
* {"p":[],"a":["href","title"],"strong":[],...}
|
||||
* where each value is the array of attribute names allowed for that tag. */
|
||||
el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json);
|
||||
el_val_t html_raw(el_val_t s);
|
||||
el_val_t html_escape(el_val_t s);
|
||||
|
||||
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
||||
|
||||
|
||||
+10062
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user