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);
|
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 ────────────────────────────────────────────────
|
/* ── HTML allowlist sanitizer ────────────────────────────────────────────────
|
||||||
* el_html_sanitize(input, allowlist_json)
|
* 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":[],...}
|
* {"p":[],"a":["href","title"],"strong":[],...}
|
||||||
* where each value is the array of attribute names allowed for that tag. */
|
* 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 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 ──────────────────────────────────────────────────────────── */
|
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
|||||||
+10062
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user