sc-theme-builder/lib/sanitize.js
2026-07-01 20:07:28 -05:00

57 lines
1.9 KiB
JavaScript

// Robustness guards shared by compile.js (overlay) AND sassCompile.js (Phase 2).
// NOT a security boundary (see README SECURITY section): an admin can already
// inject site CSS/JS by other means. These exist solely so a single malformed
// token value cannot white-screen every page -- a value that could close a CSS
// block early ("}"), terminate a declaration (";"), or open/close a comment is
// DROPPED from output (not escaped), confining each token to its own declaration.
const { MAX_VALUE_LEN } = require("./tokenSchema");
// Substrings that would let a value escape its own declaration/block.
const BAD_SUBSTRINGS = ["{", "}", ";", "</", "/*", "*/"];
// Returns the value unchanged if safe to emit, or null if it must be dropped.
// When dropped, pushes a human-readable note onto the optional warnings array.
function sanitizeValue(value, warnings) {
if (value == null) {
return null;
}
const v = String(value);
if (v.length > MAX_VALUE_LEN) {
if (warnings) {
warnings.push(`value dropped (too long): ${v.slice(0, 24)}...`);
}
return null;
}
for (const bad of BAD_SUBSTRINGS) {
if (v.includes(bad)) {
if (warnings) {
warnings.push(`value dropped (illegal "${bad}")`);
}
return null;
}
}
return v;
}
// Returns a conservative selector unchanged, or null if it could break out of a
// rule block. Selectors come only from TOKEN_SCHEMA (trusted), but we guard
// anyway so a future data-driven selector cannot escape.
function sanitizeSelector(selector) {
if (selector == null) {
return null;
}
const s = String(selector);
if (s.length > 256) {
return null;
}
if (/[{};<>]/.test(s) || s.includes("/*") || s.includes("*/")) {
return null;
}
return s;
}
module.exports = { sanitizeValue, sanitizeSelector };