shtml.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. 'use strict';
  2. const isSafeDomain = require('../utils').isSafeDomain;
  3. const xss = require('xss');
  4. const BUILD_IN_ON_TAG_ATTR = Symbol('buildInOnTagAttr');
  5. const utils = require('../utils');
  6. // default rule: https://github.com/leizongmin/js-xss/blob/master/lib/default.js
  7. // add domain filter based on xss module
  8. // custom options http://jsxss.com/zh/options.html
  9. // eg: support a tag,filter attributes except for title : whiteList: {a: ['title']}
  10. module.exports = function shtml(val) {
  11. if (typeof val !== 'string') return val;
  12. const securityOptions = this.ctx.securityOptions || {};
  13. const shtmlConfig = utils.merge(this.app.config.helper.shtml, securityOptions.shtml);
  14. const domainWhiteList = this.app.config.security.domainWhiteList;
  15. const app = this.app;
  16. // filter href and src attribute if not in domain white list
  17. if (!shtmlConfig[BUILD_IN_ON_TAG_ATTR]) {
  18. shtmlConfig[BUILD_IN_ON_TAG_ATTR] = function(tag, name, value, isWhiteAttr) {
  19. if (isWhiteAttr && (name === 'href' || name === 'src')) {
  20. if (!value) {
  21. return;
  22. }
  23. value = String(value);
  24. if (value[0] === '/' || value[0] === '#') {
  25. return;
  26. }
  27. const hostname = utils.getFromUrl(value, 'hostname');
  28. if (!hostname) {
  29. return;
  30. }
  31. // If we don't have our hostname in the app.security.domainWhiteList,
  32. // Just check for `shtmlConfig.domainWhiteList` and `ctx.whiteList`.
  33. if (!isSafeDomain(hostname, domainWhiteList)) {
  34. // Check for `shtmlConfig.domainWhiteList` first (duplicated now)
  35. if (shtmlConfig.domainWhiteList && shtmlConfig.domainWhiteList.length !== 0) {
  36. app.deprecate('[egg-security] `config.helper.shtml.domainWhiteList` has been deprecate. Please use `config.security.domainWhiteList` instead.');
  37. shtmlConfig.domainWhiteList = shtmlConfig.domainWhiteList.map(domain => domain.toLowerCase());
  38. if (!isSafeDomain(hostname, shtmlConfig.domainWhiteList)) {
  39. return '';
  40. }
  41. } else {
  42. return '';
  43. }
  44. }
  45. }
  46. };
  47. // avoid overriding user configuration 'onTagAttr'
  48. if (shtmlConfig.onTagAttr) {
  49. const original = shtmlConfig.onTagAttr;
  50. shtmlConfig.onTagAttr = function() {
  51. const result = original.apply(this, arguments);
  52. if (result !== undefined) {
  53. return result;
  54. }
  55. return shtmlConfig[BUILD_IN_ON_TAG_ATTR].apply(this, arguments);
  56. };
  57. } else {
  58. shtmlConfig.onTagAttr = shtmlConfig[BUILD_IN_ON_TAG_ATTR];
  59. }
  60. }
  61. return xss(val, shtmlConfig);
  62. };