123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*!
- * @esri/arcgis-html-sanitizer - v3.0.1 - Tue Nov 15 2022 09:46:54 GMT-0800 (Pacific Standard Time)
- * Copyright (c) 2022 - Environmental Systems Research Institute, Inc.
- * Apache-2.0
- *
- * js-xss
- * Copyright (c) 2012-2018 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
- * http://ucdok.com
- * MIT License, see https://github.com/leizongmin/js-xss/blob/master/LICENSE for details
- */
- 'use strict';
- Object.defineProperty(exports, '__esModule', { value: true });
- var xss = require('xss');
- function _interopNamespace(e) {
- if (e && e.__esModule) return e;
- var n = Object.create(null);
- if (e) {
- Object.keys(e).forEach(function (k) {
- if (k !== 'default') {
- var d = Object.getOwnPropertyDescriptor(e, k);
- Object.defineProperty(n, k, d.get ? d : {
- enumerable: true,
- get: function () { return e[k]; }
- });
- }
- });
- }
- n["default"] = e;
- return Object.freeze(n);
- }
- var xss__namespace = /*#__PURE__*/_interopNamespace(xss);
- /**
- * Determine if the value is a plain object.
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
- */
- var isPlainObject = function (value) {
- if (typeof value !== "object" || value === null) {
- return false;
- }
- if (Object.prototype.toString.call(value) !== "[object Object]") {
- return false;
- }
- var proto = Object.getPrototypeOf(value);
- if (proto === null) {
- return true;
- }
- while (Object.getPrototypeOf(proto) !== null) {
- proto = Object.getPrototypeOf(proto);
- }
- return Object.getPrototypeOf(value) === proto;
- };
- /* Copyright (c) 2020 Environmental Systems Research Institute, Inc.
- * Apache-2.0
- *
- * js-xss
- * Copyright (c) 2012-2018 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
- * http://ucdok.com
- * The MIT License, see
- * https://github.com/leizongmin/js-xss/blob/master/LICENSE for details
- * */
- /**
- * The Sanitizer Class
- *
- * @export
- * @class Sanitizer
- */
- var Sanitizer = /** @class */ (function () {
- function Sanitizer(filterOptions, extendDefaults) {
- var _this = this;
- // Supported HTML Spec: https://doc.arcgis.com/en/arcgis-online/reference/supported-html.htm
- this.arcgisWhiteList = {
- a: ["href", "style", "target"],
- abbr: ["title"],
- audio: ["autoplay", "controls", "loop", "muted", "preload"],
- b: [],
- br: [],
- dd: ["style"],
- div: ["align", "style"],
- dl: ["style"],
- dt: ["style"],
- em: [],
- figcaption: ["style"],
- figure: ["style"],
- font: ["color", "face", "size", "style"],
- h1: ["style"],
- h2: ["style"],
- h3: ["style"],
- h4: ["style"],
- h5: ["style"],
- h6: ["style"],
- hr: [],
- i: [],
- img: ["alt", "border", "height", "src", "style", "width"],
- li: [],
- ol: [],
- p: ["style"],
- source: ["media", "src", "type"],
- span: ["style"],
- strong: [],
- sub: ["style"],
- sup: ["style"],
- table: ["border", "cellpadding", "cellspacing", "height", "style", "width"],
- tbody: [],
- tr: ["align", "height", "style", "valign"],
- td: [
- "align",
- "colspan",
- "height",
- "nowrap",
- "rowspan",
- "style",
- "valign",
- "width",
- ],
- th: [
- "align",
- "colspan",
- "height",
- "nowrap",
- "rowspan",
- "style",
- "valign",
- "width",
- ],
- u: [],
- ul: [],
- video: [
- "autoplay",
- "controls",
- "height",
- "loop",
- "muted",
- "poster",
- "preload",
- "width",
- ],
- };
- this.allowedProtocols = [
- "http",
- "https",
- "mailto",
- "iform",
- "tel",
- "flow",
- "lfmobile",
- "arcgis-navigator",
- "arcgis-appstudio-player",
- "arcgis-survey123",
- "arcgis-collector",
- "arcgis-workforce",
- "arcgis-explorer",
- "arcgis-trek2there",
- "arcgis-quickcapture",
- "mspbi",
- "comgooglemaps",
- "pdfefile",
- "pdfehttp",
- "pdfehttps",
- "boxapp",
- "boxemm",
- "awb",
- "awbs",
- "gropen",
- "radarscope",
- ];
- this.arcgisFilterOptions = {
- allowCommentTag: true,
- safeAttrValue: function (tag, name, value, cssFilter) {
- // Take over safe attribute filtering for `a` `href`, `img` `src`,
- // and `source` `src` attributes, otherwise pass onto the
- // default `XSS.safeAttrValue` method.
- if ((tag === "a" && name === "href") ||
- ((tag === "img" || tag === "source") && name === "src")) {
- return _this.sanitizeUrl(value);
- }
- return xss__namespace.safeAttrValue(tag, name, value, cssFilter);
- },
- };
- this._entityMap = {
- "&": "8",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "/": "/",
- };
- var xssFilterOptions;
- if (filterOptions && !extendDefaults) {
- // Override the defaults
- xssFilterOptions = filterOptions;
- }
- else if (filterOptions && extendDefaults) {
- // Extend the defaults
- xssFilterOptions = Object.create(this.arcgisFilterOptions);
- Object.keys(filterOptions).forEach(function (key) {
- if (key === "whiteList") {
- // Extend the whitelist by concatenating arrays
- xssFilterOptions.whiteList = _this._extendObjectOfArrays([
- _this.arcgisWhiteList,
- filterOptions.whiteList || {},
- ]);
- }
- else {
- xssFilterOptions[key] = filterOptions[key];
- }
- });
- }
- else {
- // Only use the defaults
- xssFilterOptions = Object.create(this.arcgisFilterOptions);
- xssFilterOptions.whiteList = this.arcgisWhiteList;
- }
- this.xssFilterOptions = xssFilterOptions;
- // Make this readable to tests
- this._xssFilter = new xss__namespace.FilterXSS(xssFilterOptions);
- }
- /**
- * Sanitizes value to remove invalid HTML tags.
- *
- * Note: If the value passed does not contain a valid JSON data type (String,
- * Number, JSON Object, Array, Boolean, or null), the value will be nullified.
- *
- * @param {any} value The value to sanitize.
- * @returns {any} The sanitized value.
- * @memberof Sanitizer
- */
- Sanitizer.prototype.sanitize = function (value, options) {
- if (options === void 0) { options = {}; }
- switch (typeof value) {
- case "number":
- if (isNaN(value) || !isFinite(value)) {
- return null;
- }
- return value;
- case "boolean":
- return value;
- case "string":
- return this._xssFilter.process(value);
- case "object":
- return this._iterateOverObject(value, options);
- default:
- if (options.allowUndefined && typeof value === "undefined") {
- return;
- }
- return null;
- }
- };
- /**
- * Sanitizes a URL string following the allowed protocols and sanitization rules.
- *
- * @param {string} value The URL to sanitize.
- * @param {{ isProtocolRequired: boolean }} options Configuration options for URL checking.
- * @returns {string} The sanitized URL if it's valid, or an empty string if the URL is invalid.
- */
- Sanitizer.prototype.sanitizeUrl = function (value, options) {
- var _a = (options !== null && options !== void 0 ? options : {}).isProtocolRequired, isProtocolRequired = _a === void 0 ? true : _a;
- var protocol = this._trim(value.substring(0, value.indexOf(":")));
- var isRootUrl = value === '/';
- var isUrlFragment = /^#/.test(value);
- var isValidProtocol = protocol && this.allowedProtocols.indexOf(protocol.toLowerCase()) > -1;
- if (isRootUrl || isUrlFragment || isValidProtocol) {
- return xss__namespace.escapeAttrValue(value);
- }
- if (!protocol && !isProtocolRequired) {
- return xss__namespace.escapeAttrValue("https://".concat(value));
- }
- return "";
- };
- /**
- * Sanitizes an HTML attribute value.
- *
- * @param {string} tag The tagname of the HTML element.
- * @param {string} attribute The attribute name of the HTML element.
- * @param {string} value The raw value to be used for the HTML attribute value.
- * @param {XSS.ICSSFilter} [cssFilter] The CSS filter to be used.
- * @returns {string} The sanitized attribute value.
- * @memberof Sanitizer
- */
- Sanitizer.prototype.sanitizeHTMLAttribute = function (tag, attribute, value, cssFilter) {
- // use the custom safeAttrValue function if provided
- if (typeof this.xssFilterOptions.safeAttrValue === "function") {
- return this.xssFilterOptions.safeAttrValue(tag, attribute, value,
- // @ts-expect-error safeAttrValue does handle undefined cssFilter
- cssFilter);
- }
- // otherwise use the default
- // @ts-ignore safeAttrValue does handle undefined cssFilter
- return xss__namespace.safeAttrValue(tag, attribute, value, cssFilter);
- };
- /**
- * Checks if a value only contains valid HTML.
- *
- * @param {any} value The value to validate.
- * @returns {boolean}
- * @memberof Sanitizer
- */
- Sanitizer.prototype.validate = function (value, options) {
- if (options === void 0) { options = {}; }
- var sanitized = this.sanitize(value, options);
- return {
- isValid: value === sanitized,
- sanitized: sanitized,
- };
- };
- /**
- * Encodes the following characters, `& < > \" ' /` to their hexadecimal HTML entity code.
- * Example: "·" => "8middot;"
- *
- * @param {string} value The value to encode.
- * @returns {string} The encoded string value.
- * @memberof Sanitizer
- */
- Sanitizer.prototype.encodeHTML = function (value) {
- var _this = this;
- return String(value).replace(/[&<>"'\/]/g, function (s) {
- return _this._entityMap[s];
- });
- };
- /**
- * Encodes all non-alphanumeric ASCII characters to their hexadecimal HTML entity codes.
- * Example: "alert(document.cookie)" => "alert(document.cookie)"
- *
- * @param {string} value The value to encode.
- * @returns {string} The encoded string value.
- * @memberof Sanitizer
- */
- Sanitizer.prototype.encodeAttrValue = function (value) {
- var alphanumericRE = /^[a-zA-Z0-9]$/;
- return String(value).replace(/[\x00-\xFF]/g, function (c, idx) {
- return !alphanumericRE.test(c)
- ? "&#x".concat(Number(value.charCodeAt(idx)).toString(16), ";")
- : c;
- });
- };
- /**
- * Extends an object of arrays by by concatenating arrays of the same object
- * keys. If the if the previous key's value is not an array, the next key's
- * value will replace the previous key. This method is used for extending the
- * whiteList in the XSS filter options.
- *
- * @private
- * @param {Array<{}>} objects An array of objects.
- * @returns {{}} The extended object.
- * @memberof Sanitizer
- */
- Sanitizer.prototype._extendObjectOfArrays = function (objects) {
- var finalObj = {};
- objects.forEach(function (obj) {
- Object.keys(obj).forEach(function (key) {
- if (Array.isArray(obj[key]) && Array.isArray(finalObj[key])) {
- finalObj[key] = finalObj[key].concat(obj[key]);
- }
- else {
- finalObj[key] = obj[key];
- }
- });
- });
- return finalObj;
- };
- /**
- * Iterate over a plain object or array to deeply sanitize each value.
- *
- * @private
- * @param {object} obj The object to iterate over.
- * @returns {(object | null)} The sanitized object.
- * @memberof Sanitizer
- */
- Sanitizer.prototype._iterateOverObject = function (obj, options) {
- var _this = this;
- if (options === void 0) { options = {}; }
- try {
- var hasChanged_1 = false;
- var changedObj = void 0;
- if (Array.isArray(obj)) {
- changedObj = obj.reduce(function (prev, value) {
- var validation = _this.validate(value, options);
- if (validation.isValid) {
- return prev.concat([value]);
- }
- else {
- hasChanged_1 = true;
- return prev.concat([validation.sanitized]);
- }
- }, []);
- }
- else if (!isPlainObject(obj)) {
- if (options.allowUndefined && typeof obj === "undefined") {
- return;
- }
- return null;
- }
- else {
- var keys = Object.keys(obj);
- changedObj = keys.reduce(function (prev, key) {
- var value = obj[key];
- var validation = _this.validate(value, options);
- if (validation.isValid) {
- prev[key] = value;
- }
- else {
- hasChanged_1 = true;
- prev[key] = validation.sanitized;
- }
- return prev;
- }, {});
- }
- if (hasChanged_1) {
- return changedObj;
- }
- return obj;
- }
- catch (err) {
- return null;
- }
- };
- /**
- * Trim whitespace from the start and ends of a string.
- * @param {string} val The string to trim.
- * @returns {string} The trimmed string.
- */
- Sanitizer.prototype._trim = function (val) {
- // @ts-ignore This is used by Jest,
- // but TypeScript errors since it assumes `trim` is always available.
- return String.prototype.trim
- ? val.trim()
- : val.replace(/(^\s*)|(\s*$)/g, "");
- };
- return Sanitizer;
- }());
- exports.Sanitizer = Sanitizer;
- exports["default"] = Sanitizer;
|