Repository navigation
Production security & quality hardening (0.8.0) - #74
Merged
Merged
Conversation
Prepares zigmark for public release / untrusted input (PolicyPress). HTML XSS: - Filter dangerous URL schemes (javascript:/vbscript:/file:/non-image data:) by default in links, images, and autolinks, with browser-style scheme normalisation; denylist keeps CommonMark/GFM conformance (652/652 + 24/24). - Escape footnote labels (were injected raw into attribute + element text). - Add opt-in safe mode (html.Options.safe / renderHtmlWithOptions / zigmark_render_html_safe / CLI --safe) that escapes raw HTML to visible text. Typst injection (compile-time file read): - Emit code spans/blocks via raw() with string-literal escaping. - Validate frontmatter-derived preamble fields (length/hex/string) with safe fallbacks. DoS / panics: - Parser.max_nesting_depth (128) and max_input_bytes (16 MiB) turn pathological input into NestingTooDeep / InputTooLarge instead of stack overflow / OOM; error paths free the partial tree. - Fix ATX '#' u8 overflow and frontmatter @intcast panics (std.math.cast). Frontmatter (#73): - YAML parse failure no longer leaks the error bundle, asserts, or writes to stderr from library code; CLI degrades gracefully. (Underlying plain-scalar " - " rejection is an upstream zig-yaml limitation.) Tooling: - Fix fuzz harness (was broken on Zig 0.16: old []const u8 signature -> *std.testing.Smith); add safe-HTML/Typst/ZON targets; CI fuzz smoke check. - Add zig fmt --check gate; guard deliberate hand-alignment with // zig fmt: off/on. - New src/markdown/security_test.zig regression suite.
sc2ben
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Readies zigmark for public release and untrusted-input use (PolicyPress), mirroring the pozeiden v0.3.0 hardening run. All findings were confirmed at the source and locked in with a regression suite. CommonMark/GFM conformance is preserved (652/652 + 24/24), and
om cipasses (test, fuzz, fmt, wasm).HTML — XSS
javascript:/vbscript:/file:/non-imagedata:destinations in links, images, and autolinks render with an emptyhref/src. Detection normalises the scheme like a browser (leading control/space stripping, single-byte entity decoding sojavascript:is caught, tab/newline/CR stripping). A denylist, somade-up-scheme://anddata:image/{png,gif,jpeg,webp}still pass — conformance unchanged.safemode escapes raw HTML to visible text:html.Options{ .safe = true },zigmark.renderHtmlWithOptions, thezigmark_render_html_safeC ABI export, and the CLI--safeflag. Default output is byte-identical.Typst — code injection (compile-time file read)
raw()with string-literal escaping, so backticks/fences can't break out into executable Typst.fontsizeas a length, colours as hex, strings escaped) with safe fallbacks. The unquotedfontsizewas the strongest injection candidate.DoS / panics
Parser.max_nesting_depth(128) andParser.max_input_bytes(16 MiB,0= unlimited) turn deeply-nested / oversized input intoerror.NestingTooDeep/error.InputTooLargeinstead of stack overflow / unbounded memory. Error paths free the partial tree (leak-checked understd.testing.allocator).#u8overflow and the frontmatter@intCastpanics.Frontmatter (#73)
frontmatter/markdownformats degrade gracefully instead of aborting." - "is an upstreamzig-yamlbug; documented as needing a fork-level fix.Tooling
fn (void, []const u8)signature and never compiled against Zig 0.16'sstd.testing.fuzz; now uses*std.testing.Smith. Added targets for safe-mode HTML, Typst, and ZON; wired azig build fuzzsmoke check into CI. Fixed thefuzz--fuzztypo in the dev-shell app.zig fmt --checkgate added as a flake check. Deliberate hand-alignment is preserved with// zig fmt: off/onguards; incidental formatting normalised.src/markdown/security_test.zigregression suite (run byzig build test).Docs / release
### Securitysection, README "Rendering Untrusted Markdown" section, SECURITY.md guidance + known limitations, version0.7.4→0.8.0.Known limitation
The inline link/emphasis scanner is O(n²) on pathological unmatched-bracket input — a CPU-DoS bounded (not eliminated) by
max_input_bytes. A delimiter-stack rewrite is deferred to a follow-up; untrusted-input deployments should set a conservativemax_input_bytesand a call-site timeout.