Skip to content

Commit 307019c

Browse files
committed
Better rendering of weird characters
1 parent 6e6cbff commit 307019c

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

src/curl_fuzzer_tools/templates/corpus_decoder.html

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
word-break: break-word;
136136
}
137137

138+
/* Subtle styling for visualization glyphs (interpunct, NUL, etc.) */
139+
.viz-char {
140+
opacity: 0.3;
141+
}
142+
138143
.label {
139144
display: inline-flex;
140145
align-items: center;
@@ -454,7 +459,8 @@ <h2 id="summary-header">Summary</h2>
454459
const decoder = new TextDecoder("utf-8", { fatal: false });
455460
const MAX_PREVIEW = 512;
456461
const MAX_HEX_BYTES = 256;
457-
const INTERPUNCT = "\u00B7";
462+
const INTERPUNCT = "\u00B7"; // · (space/whitespace visualizer)
463+
const NULL_SYMBOL = "\u2300"; // ⌀ (lighter null marker)
458464

459465
const elements = {
460466
fileInput: document.getElementById("corpus-input"),
@@ -482,24 +488,42 @@ <h2 id="summary-header">Summary</h2>
482488
}
483489

484490
function sanitizeString(bytes) {
491+
// Return safe HTML with visualization glyphs wrapped in a span for reduced opacity.
485492
if (!bytes || !bytes.length) {
486493
return "";
487494
}
488495
try {
489496
const truncated = bytes.length > MAX_PREVIEW ? bytes.slice(0, MAX_PREVIEW) : bytes;
490497
const decoded = decoder.decode(truncated);
491-
return visualizeWhitespace(decoded);
498+
return renderVisibleHTML(decoded);
492499
} catch (err) {
493500
console.debug("Failed to decode as UTF-8", err);
494501
return "";
495502
}
496503
}
497504

498-
function visualizeWhitespace(text) {
499-
if (!text) {
500-
return "";
505+
function escapeHTML(s) {
506+
return s.replace(/[&<>"']/g, (ch) => (
507+
ch === '&' ? '&amp;' : ch === '<' ? '&lt;' : ch === '>' ? '&gt;' : ch === '"' ? '&quot;' : '&#39;'
508+
));
509+
}
510+
511+
function renderVisibleHTML(text) {
512+
if (!text) return "";
513+
// Build escaped HTML, substituting visualization spans for NULs and whitespace.
514+
let out = "";
515+
for (let i = 0; i < text.length; i += 1) {
516+
const ch = text[i];
517+
const code = text.charCodeAt(i);
518+
if (code === 0x0000) {
519+
out += `<span class="viz-char" title="NUL (0x00)">${NULL_SYMBOL}</span>`;
520+
} else if (ch === ' ' || ch === '\t' || ch === '\v' || ch === '\f' || ch === '\r' || ch === '\u00A0') {
521+
out += `<span class="viz-char" title="Whitespace">${INTERPUNCT}</span>`;
522+
} else {
523+
out += escapeHTML(ch);
524+
}
501525
}
502-
return text.replace(/[\u00A0 \t\v\f\r]/g, INTERPUNCT);
526+
return out;
503527
}
504528

505529
function toHex(bytes) {
@@ -573,7 +597,7 @@ <h2 id="summary-header">Summary</h2>
573597
const previewCell = document.createElement("td");
574598
previewCell.className = "mono";
575599
previewCell.dataset.label = "Preview";
576-
previewCell.textContent = row.preview || "\u00a0";
600+
previewCell.innerHTML = row.preview || "\u00a0";
577601

578602
const hexCell = document.createElement("td");
579603
hexCell.className = "mono";

0 commit comments

Comments
 (0)