next-csrf
Version:
CSRF mitigation library for Next.js
195 lines (194 loc) • 5.62 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
Module dependencies
*/
var ElementType = __importStar(require("domelementtype"));
var entities = __importStar(require("entities"));
/* mixed-case SVG and MathML tags & attributes
recognized by the HTML parser, see
https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign
*/
var foreignNames_1 = require("./foreignNames");
var unencodedElements = new Set([
"style",
"script",
"xmp",
"iframe",
"noembed",
"noframes",
"plaintext",
"noscript",
]);
/*
Format attributes
*/
function formatAttributes(attributes, opts) {
if (!attributes)
return;
return Object.keys(attributes)
.map(function (key) {
var _a;
var value = attributes[key];
if (opts.xmlMode === "foreign") {
/* fix up mixed-case attribute names */
key = (_a = foreignNames_1.attributeNames.get(key)) !== null && _a !== void 0 ? _a : key;
}
if (!opts.emptyAttrs &&
!opts.xmlMode &&
(value === null || value === "")) {
return key;
}
return key + "=\"" + (opts.decodeEntities
? entities.encodeXML(value)
: value.replace(/"/g, """)) + "\"";
})
.join(" ");
}
/*
Self-enclosing tags
*/
var singleTag = new Set([
"area",
"base",
"basefont",
"br",
"col",
"command",
"embed",
"frame",
"hr",
"img",
"input",
"isindex",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr",
]);
/**
* Renders a DOM node or an array of DOM nodes to a string.
*
* Can be thought of as the equivalent of the `outerHTML` of the passed node(s).
*
* @param node Node to be rendered.
* @param options Changes serialization behavior
*/
function render(node, options) {
if (options === void 0) { options = {}; }
// TODO: This is a bit hacky.
var nodes = Array.isArray(node) || node.cheerio ? node : [node];
var output = "";
for (var i = 0; i < nodes.length; i++) {
output += renderNode(nodes[i], options);
}
return output;
}
exports.default = render;
function renderNode(node, options) {
switch (node.type) {
case "root":
return render(node.children, options);
case ElementType.Directive:
return renderDirective(node);
case ElementType.Comment:
return renderComment(node);
case ElementType.CDATA:
return renderCdata(node);
default:
return ElementType.isTag(node)
? renderTag(node, options)
: renderText(node, options);
}
}
var foreignModeIntegrationPoints = new Set([
"mi",
"mo",
"mn",
"ms",
"mtext",
"annotation-xml",
"foreignObject",
"desc",
"title",
]);
var foreignElements = new Set(["svg", "math"]);
function renderTag(elem, opts) {
var _a;
// Handle SVG / MathML in HTML
if (opts.xmlMode === "foreign") {
/* fix up mixed-case element names */
elem.name = (_a = foreignNames_1.elementNames.get(elem.name)) !== null && _a !== void 0 ? _a : elem.name;
/* exit foreign mode at integration points */
if (elem.parent &&
foreignModeIntegrationPoints.has(elem.parent.name))
opts = __assign(__assign({}, opts), { xmlMode: false });
}
if (!opts.xmlMode && foreignElements.has(elem.name)) {
opts = __assign(__assign({}, opts), { xmlMode: "foreign" });
}
var tag = "<" + elem.name;
var attribs = formatAttributes(elem.attribs, opts);
if (attribs) {
tag += " " + attribs;
}
if ((!elem.children || elem.children.length === 0) &&
(opts.xmlMode
? // in XML mode or foreign mode, and user hasn't explicitly turned off self-closing tags
opts.selfClosingTags !== false
: // user explicitly asked for self-closing tags, even in HTML mode
opts.selfClosingTags && singleTag.has(elem.name))) {
if (!opts.xmlMode)
tag += " ";
tag += "/>";
}
else {
tag += ">";
if (elem.children) {
tag += render(elem.children, opts);
}
if (opts.xmlMode || !singleTag.has(elem.name)) {
tag += "</" + elem.name + ">";
}
}
return tag;
}
function renderDirective(elem) {
return "<" + elem.data + ">";
}
function renderText(elem, opts) {
var data = elem.data || "";
// if entities weren't decoded, no need to encode them back
if (opts.decodeEntities &&
!(elem.parent && unencodedElements.has(elem.parent.name))) {
data = entities.encodeXML(data);
}
return data;
}
function renderCdata(elem) {
return "<![CDATA[" + elem.children[0].data + "]]>";
}
function renderComment(elem) {
return "<!--" + elem.data + "-->";
}