#ifndef CST_WEB_MD_H #define CST_WEB_MD_H /* Markdown for the WebUI: the same sashidown renderer the TUI uses, but * HTML output with a strict allowlist. * * sashidown renders straight to HTML and escapes text, quotes and '&', so * no raw markup can reach the browser. Two holes remain, and both matter * because every byte rendered here comes from a model or a tool: a link's * href keeps any scheme ("javascript:") and an image's src may point * anywhere (a tracking pixel, or worse). cst_web_md_* therefore rewrites * tags instead of passing them through: unknown tags are dropped, hrefs * are limited to http/https/mailto/relative, and images to the session's * own attachment endpoint. * * The streaming form renders confirmed HTML as the model talks, which is * the CST property the project is built on: nothing is emitted before it * is safe to emit. A tag split across two deltas is held back until its * '>' arrives. */ #include #ifdef __cplusplus extern "C" { #endif /* Called with chunks of sanitized HTML; return non-zero to stop. */ typedef int (*cst_web_md_emit_fn)(const char *html, size_t len, void *ud); typedef struct cst_web_md cst_web_md_t; cst_web_md_t *cst_web_md_new(cst_web_md_emit_fn emit, void *ud); /* Feed model output. Returns 0, or -1 on OOM. */ int cst_web_md_feed(cst_web_md_t *s, const char *data, size_t len); /* Flush the pending input and close the renderer. */ int cst_web_md_finish(cst_web_md_t *s); void cst_web_md_free(cst_web_md_t *s); /* One-shot: markdown -> sanitized HTML (malloc'd, never NULL on success). */ char *cst_web_md_html(const char *md, size_t len); /* The sanitizer alone (tests, and any HTML that did not come from here). */ char *cst_web_md_sanitize(const char *html, size_t len); #ifdef __cplusplus } #endif #endif /* CST_WEB_MD_H */