mirror of
https://github.com/wassname/talk.git
synced 2026-08-11 11:27:10 +08:00
[next] DOM Purification (#2115)
* feat: added purify + linkify + better link detection * fix: linting + tests * chore: refactor RequireProperty Type
This commit is contained in:
Generated
+122
-90
@@ -1882,12 +1882,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/dompurify": {
|
||||
"version": "0.0.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-0.0.31.tgz",
|
||||
"integrity": "sha512-Vi2VBl262MjJuxpROjQU3AuBv1NgA7oaL9jqLJRgF60tps4GR/9qzXN0P4pRYPXsBA0b10AF1M6wO74uwGqtmw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/dotenv": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-4.0.3.tgz",
|
||||
@@ -2055,9 +2049,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/jsdom": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-11.0.6.tgz",
|
||||
"integrity": "sha512-6sw9iNdXak3Dk4iN1osdZeZcV8cTe2SNIVwbnt8MjFFAI8+nBWDNcDfjUgZZqycF3Twlr7xXlKntnOrruxNRvg==",
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-11.12.0.tgz",
|
||||
"integrity": "sha512-XHMNZFQ0Ih3A4/NTWAO15+OsQafPKnQCanN0FYGbsTM/EoI5EoEAvvkF51/DQC2BT5low4tomp7k2RLMlriA5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/events": "*",
|
||||
@@ -2081,6 +2075,12 @@
|
||||
"integrity": "sha512-WRj1rJPkj3fyDME63xUkWvcWzq0XjpiqjJGNCH4Y6Fbiv2fVMDCqeIA6ch/UUG3VYrfGq1VNFLsz6Sat6FjsPw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/linkifyjs": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkifyjs/-/linkifyjs-2.1.0.tgz",
|
||||
"integrity": "sha512-iPV2Ha51/vNZrleyRWaw4MHxL4gw5li0vkE4K7eT9e7kBEDv1YN7GIFJZj3PrFiTrUGobk7P92lgtGFhrS5NHw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.118",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.118.tgz",
|
||||
@@ -2352,9 +2352,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/tough-cookie": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz",
|
||||
"integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==",
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.4.tgz",
|
||||
"integrity": "sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/uglify-js": {
|
||||
@@ -2870,9 +2870,9 @@
|
||||
"integrity": "sha1-OOgxnXk3niRiiEW1O5ZyKyng5Hw="
|
||||
},
|
||||
"abab": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
|
||||
"integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==",
|
||||
"dev": true
|
||||
},
|
||||
"abbrev": {
|
||||
@@ -2906,14 +2906,29 @@
|
||||
}
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz",
|
||||
"integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz",
|
||||
"integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^5.0.0"
|
||||
"acorn": "^6.0.1",
|
||||
"acorn-walk": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
|
||||
"integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
|
||||
"integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==",
|
||||
"dev": true
|
||||
},
|
||||
"address": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz",
|
||||
@@ -5775,9 +5790,9 @@
|
||||
}
|
||||
},
|
||||
"browser-process-hrtime": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
|
||||
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=",
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
|
||||
"integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==",
|
||||
"dev": true
|
||||
},
|
||||
"browser-resolve": {
|
||||
@@ -7887,9 +7902,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"cssstyle": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz",
|
||||
"integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz",
|
||||
"integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssom": "0.3.x"
|
||||
@@ -7934,14 +7949,27 @@
|
||||
}
|
||||
},
|
||||
"data-urls": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz",
|
||||
"integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
|
||||
"integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"abab": "^1.0.4",
|
||||
"whatwg-mimetype": "^2.0.0",
|
||||
"whatwg-url": "^6.4.0"
|
||||
"abab": "^2.0.0",
|
||||
"whatwg-mimetype": "^2.2.0",
|
||||
"whatwg-url": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"whatwg-url": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
|
||||
"integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"tr46": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataloader": {
|
||||
@@ -9454,9 +9482,9 @@
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.7.tgz",
|
||||
"integrity": "sha512-1xK0JEda/jvIm3SgqHXKvRCh3AbEKCyBbUAGpNCMVIljBD145cPvBR66JSj3O4SdscFUx5NXsDkJpz6vDT8KLg==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.8.tgz",
|
||||
"integrity": "sha512-vetRFbN1SXSPfP3ClIiYnxTrXquSqakBEOoB5JESn0SVcSYzpu6ougjakpKnskGctYdlNpwf+riUHSkG7d4XUw==",
|
||||
"dev": true
|
||||
},
|
||||
"domutils": {
|
||||
@@ -12334,7 +12362,7 @@
|
||||
},
|
||||
"handle-thing": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
|
||||
"resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
|
||||
"integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=",
|
||||
"dev": true
|
||||
},
|
||||
@@ -12803,7 +12831,7 @@
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
|
||||
"integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -15288,6 +15316,12 @@
|
||||
"topo": "3.x.x"
|
||||
}
|
||||
},
|
||||
"jquery": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
|
||||
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==",
|
||||
"optional": true
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz",
|
||||
@@ -15315,49 +15349,37 @@
|
||||
"optional": true
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "11.11.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz",
|
||||
"integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==",
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
|
||||
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"abab": "^1.0.4",
|
||||
"acorn": "^5.3.0",
|
||||
"abab": "^2.0.0",
|
||||
"acorn": "^5.5.3",
|
||||
"acorn-globals": "^4.1.0",
|
||||
"array-equal": "^1.0.0",
|
||||
"cssom": ">= 0.3.2 < 0.4.0",
|
||||
"cssstyle": ">= 0.3.1 < 0.4.0",
|
||||
"cssstyle": "^1.0.0",
|
||||
"data-urls": "^1.0.0",
|
||||
"domexception": "^1.0.0",
|
||||
"escodegen": "^1.9.0",
|
||||
"domexception": "^1.0.1",
|
||||
"escodegen": "^1.9.1",
|
||||
"html-encoding-sniffer": "^1.0.2",
|
||||
"left-pad": "^1.2.0",
|
||||
"nwsapi": "^2.0.0",
|
||||
"left-pad": "^1.3.0",
|
||||
"nwsapi": "^2.0.7",
|
||||
"parse5": "4.0.0",
|
||||
"pn": "^1.1.0",
|
||||
"request": "^2.83.0",
|
||||
"request": "^2.87.0",
|
||||
"request-promise-native": "^1.0.5",
|
||||
"sax": "^1.2.4",
|
||||
"symbol-tree": "^3.2.2",
|
||||
"tough-cookie": "^2.3.3",
|
||||
"tough-cookie": "^2.3.4",
|
||||
"w3c-hr-time": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2",
|
||||
"whatwg-encoding": "^1.0.3",
|
||||
"whatwg-mimetype": "^2.1.0",
|
||||
"whatwg-url": "^6.4.1",
|
||||
"ws": "^4.0.0",
|
||||
"ws": "^5.2.0",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0",
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
@@ -15842,6 +15864,16 @@
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"linkifyjs": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-2.1.7.tgz",
|
||||
"integrity": "sha512-Cbn77BnYEslpAObZZoP6GVQHF1j5T6RsDydNq5RVxIy4eiZAiADRx7qHfWzfEMQecc1PtZFog1AsCGGX2WjQLA==",
|
||||
"requires": {
|
||||
"jquery": "^3.3.1",
|
||||
"react": "^16.4.2",
|
||||
"react-dom": "^16.4.2"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-7.3.0.tgz",
|
||||
@@ -17798,9 +17830,9 @@
|
||||
}
|
||||
},
|
||||
"nwsapi": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.4.tgz",
|
||||
"integrity": "sha512-Zt6HRR6RcJkuj5/N9zeE7FN6YitRW//hK2wTOwX274IBphbY3Zf5+yn5mZ9v/SzAOTMjQNxZf9KkmPLWn0cV4g==",
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz",
|
||||
"integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"oauth": {
|
||||
@@ -21027,9 +21059,9 @@
|
||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.1.28",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.28.tgz",
|
||||
"integrity": "sha512-+AqO1Ae+N/4r7Rvchrdm432afjT9hqJRyBN3DQv9At0tPz4hIFSGKbq64fN9dVoCow4oggIIax5/iONx0r9hZw==",
|
||||
"version": "1.1.29",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
|
||||
"integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pstree.remy": {
|
||||
@@ -21269,7 +21301,6 @@
|
||||
"version": "16.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.5.2.tgz",
|
||||
"integrity": "sha512-FDCSVd3DjVTmbEAjUNX6FgfAmQ+ypJfHUsqUJOYNCBUp1h8lqmtC+0mXJ+JjsWx4KAVTkk1vKd1hLQPvEviSuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -21582,7 +21613,6 @@
|
||||
"version": "16.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.5.2.tgz",
|
||||
"integrity": "sha512-RC8LDw8feuZOHVgzEf7f+cxBr/DnKdqp56VU0lAs1f4UfKc4cU8wU4fTq/mgnvynLQo8OtlPC19NUFh/zjZPuA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -23033,7 +23063,6 @@
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/schedule/-/schedule-0.5.0.tgz",
|
||||
"integrity": "sha512-HUcJicG5Ou8xfR//c2rPT0lPIRR09vVvN81T9fqfVgBmhERUbDEQoYKjpBxbueJnCPpSu2ujXzOnRQt6x9o/jw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
@@ -24549,21 +24578,13 @@
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.24",
|
||||
"punycode": "^1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
|
||||
"dev": true
|
||||
}
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
@@ -26729,12 +26750,23 @@
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-encoding": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz",
|
||||
"integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
|
||||
"integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"iconv-lite": "0.4.19"
|
||||
"iconv-lite": "0.4.24"
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
@@ -26744,9 +26776,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-mimetype": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz",
|
||||
"integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
|
||||
"integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-url": {
|
||||
|
||||
+5
-4
@@ -56,6 +56,7 @@
|
||||
"content-security-policy-builder": "^2.0.0",
|
||||
"convict": "^4.3.1",
|
||||
"cors": "^2.8.4",
|
||||
"dompurify": "^1.0.8",
|
||||
"dataloader": "^1.4.0",
|
||||
"dotenv": "^6.0.0",
|
||||
"dotenv-expand": "^4.2.0",
|
||||
@@ -78,6 +79,7 @@
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"jwks-rsa": "^1.3.0",
|
||||
"linkify-it": "^2.0.3",
|
||||
"linkifyjs": "^2.1.7",
|
||||
"lodash": "^4.17.10",
|
||||
"luxon": "^1.3.1",
|
||||
"metascraper-author": "^3.11.8",
|
||||
@@ -126,7 +128,6 @@
|
||||
"@types/convict": "^4.2.0",
|
||||
"@types/cors": "^2.8.4",
|
||||
"@types/cross-spawn": "^6.0.0",
|
||||
"@types/dompurify": "0.0.31",
|
||||
"@types/dotenv": "^4.0.3",
|
||||
"@types/enzyme": "^3.1.11",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.2",
|
||||
@@ -139,9 +140,10 @@
|
||||
"@types/ioredis": "^3.2.12",
|
||||
"@types/jest": "^23.1.5",
|
||||
"@types/joi": "^13.0.8",
|
||||
"@types/jsdom": "^11.0.6",
|
||||
"@types/jsdom": "^11.12.0",
|
||||
"@types/jsonwebtoken": "^7.2.7",
|
||||
"@types/linkify-it": "^2.0.3",
|
||||
"@types/linkifyjs": "^2.1.0",
|
||||
"@types/lodash": "^4.14.118",
|
||||
"@types/luxon": "^0.5.3",
|
||||
"@types/mini-css-extract-plugin": "^0.2.0",
|
||||
@@ -194,7 +196,6 @@
|
||||
"css-loader": "^0.28.11",
|
||||
"del": "^3.0.0",
|
||||
"docz": "^0.5.8",
|
||||
"dompurify": "^1.0.7",
|
||||
"enzyme": "^3.7.0",
|
||||
"enzyme-adapter-react-16": "^1.6.0",
|
||||
"enzyme-to-json": "^3.3.4",
|
||||
@@ -217,7 +218,7 @@
|
||||
"jest-junit": "^5.1.0",
|
||||
"jest-localstorage-mock": "^2.2.0",
|
||||
"jest-mock-console": "^0.4.0",
|
||||
"jsdom": "^11.11.0",
|
||||
"jsdom": "^11.12.0",
|
||||
"lint-staged": "^7.3.0",
|
||||
"loader-utils": "^1.1.0",
|
||||
"material-design-icons": "^3.0.1",
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import cn from "classnames";
|
||||
import dompurify from "dompurify";
|
||||
import React, { StatelessComponent } from "react";
|
||||
|
||||
import { createPurify } from "talk-common/utils/purify";
|
||||
|
||||
import styles from "./HTMLContent.css";
|
||||
|
||||
/**
|
||||
* Create a purify instance that will be used to handle HTML content.
|
||||
*/
|
||||
const purify = createPurify(window, false);
|
||||
|
||||
interface HTMLContentProps {
|
||||
children: string;
|
||||
className?: string;
|
||||
@@ -15,7 +21,7 @@ const HTMLContent: StatelessComponent<HTMLContentProps> = ({
|
||||
}) => (
|
||||
<div
|
||||
className={cn(styles.root, className)}
|
||||
dangerouslySetInnerHTML={{ __html: dompurify.sanitize(children) }}
|
||||
dangerouslySetInnerHTML={{ __html: purify.sanitize(children) }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ exports[`sanitizes evil html 1`] = `
|
||||
className="HTMLContent-root"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<span title=\\"test\\">Hello world</span>
|
||||
"__html": "<span>Hello world</span>
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ export type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
|
||||
|
||||
export type Sub<T, U> = Pick<T, Diff<keyof T, keyof U>>;
|
||||
|
||||
export type RequireProperty<T, P extends keyof T> = Omit<Partial<T>, P> &
|
||||
Required<Pick<T, P>>;
|
||||
|
||||
/**
|
||||
* Make all properties in T writeable
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`allows anchor links 1`] = `
|
||||
Object {
|
||||
"body": "<a href=\\"test\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">This is a link</a>",
|
||||
"linkCount": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`allows anchor tags and counts them correctly 1`] = `
|
||||
"<div>
|
||||
<a href=\\"https://mozilla.org/\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">Mozilla</a>
|
||||
<a href=\\"https://mozilla.org/\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">Mozilla</a>
|
||||
</div>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`sanitizes out attributes not allowed 1`] = `
|
||||
Object {
|
||||
"body": "<div>Test</div>",
|
||||
"linkCount": 0,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sanitizes out tags not allowed 1`] = `
|
||||
Object {
|
||||
"body": "",
|
||||
"linkCount": 0,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sanitizes out tags not allowed 2`] = `
|
||||
Object {
|
||||
"body": "",
|
||||
"linkCount": 0,
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,46 @@
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import { createPurify, sanitizeCommentBody } from "./purify";
|
||||
|
||||
const window = new JSDOM("", {}).window;
|
||||
const DOMPurify = createPurify(window);
|
||||
|
||||
it("sanitizes out tags not allowed", () => {
|
||||
expect(
|
||||
sanitizeCommentBody(
|
||||
DOMPurify,
|
||||
"<script type=\"text/javascript\">alert('ok');</script>"
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
|
||||
expect(
|
||||
sanitizeCommentBody(DOMPurify, "<script src=malicious-code.js></script>")
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("sanitizes out attributes not allowed", () => {
|
||||
expect(
|
||||
sanitizeCommentBody(DOMPurify, '<div id="test">Test</div>')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("allows anchor links", () => {
|
||||
expect(
|
||||
sanitizeCommentBody(DOMPurify, '<a href="test">This is a link</a>')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("allows anchor tags and counts them correctly", () => {
|
||||
const { body, linkCount } = sanitizeCommentBody(
|
||||
DOMPurify,
|
||||
`
|
||||
<div>
|
||||
<a href="https://mozilla.org/">Mozilla</a>
|
||||
<a href="https://mozilla.org/">Mozilla</a>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
|
||||
expect(body).toMatchSnapshot();
|
||||
expect(linkCount).toEqual(2);
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import newDOMPurify, { DOMPurify } from "dompurify";
|
||||
|
||||
export function createPurify<T extends boolean = true>(
|
||||
window: Window,
|
||||
returnDOM: T = true as T
|
||||
) {
|
||||
// Initializing JSDOM and DOMPurify
|
||||
const purify = newDOMPurify<T>(window);
|
||||
|
||||
// Setting our DOMPurify config.
|
||||
purify.setConfig({
|
||||
// Only forward anchor tags, bold, italics, blockquote, breaks, divs, and
|
||||
// spans.
|
||||
ALLOWED_TAGS: ["a", "strong", "i", "blockquote", "br", "div", "span"],
|
||||
// Only allow href tags for anchor tags.
|
||||
ALLOWED_ATTR: ["href"],
|
||||
// Always return the DOM to the caller of sanitize.
|
||||
RETURN_DOM: returnDOM,
|
||||
});
|
||||
|
||||
// Ensure that each anchor tag has a "target" and "rel" attributes set, and
|
||||
// strip the "href" attribute from all non-anchor tags.
|
||||
purify.addHook("afterSanitizeAttributes", node => {
|
||||
if (node.nodeName === "A") {
|
||||
// Ensure we wrap all the links with the target + rel set.
|
||||
node.setAttribute("target", "_blank");
|
||||
node.setAttribute("rel", "noopener noreferrer");
|
||||
} else {
|
||||
// The only tag that's allowed attributes is the "A" tag.
|
||||
node.removeAttribute("href");
|
||||
}
|
||||
});
|
||||
|
||||
return purify;
|
||||
}
|
||||
|
||||
export function sanitizeCommentBody(purify: DOMPurify, source: string) {
|
||||
// Sanitize and return the HTMLBodyElement for the parsed source.
|
||||
const sanitized = purify.sanitize(source);
|
||||
|
||||
// Count the total number of anchor links in the sanitized output, this is the
|
||||
// number of links.
|
||||
const linkCount = sanitized.getElementsByTagName("a").length;
|
||||
|
||||
return {
|
||||
body: sanitized.innerHTML,
|
||||
linkCount,
|
||||
};
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export async function create(
|
||||
}
|
||||
|
||||
// Run the comment through the moderation phases.
|
||||
const { actions, status, metadata } = await processForModeration({
|
||||
const { actions, body, status, metadata } = await processForModeration({
|
||||
story,
|
||||
tenant,
|
||||
comment: input,
|
||||
@@ -118,6 +118,7 @@ export async function create(
|
||||
// Create the comment!
|
||||
const comment = await createComment(mongo, tenant.id, {
|
||||
...input,
|
||||
body,
|
||||
status,
|
||||
grandparentIDs,
|
||||
metadata,
|
||||
@@ -234,7 +235,7 @@ export async function edit(
|
||||
}
|
||||
|
||||
// Run the comment through the moderation phases.
|
||||
const { status, metadata, actions } = await processForModeration({
|
||||
const { body, status, metadata, actions } = await processForModeration({
|
||||
story,
|
||||
tenant,
|
||||
comment: input,
|
||||
@@ -258,7 +259,7 @@ export async function edit(
|
||||
const result = await editComment(mongo, tenant.id, {
|
||||
id: input.id,
|
||||
authorID: author.id,
|
||||
body: input.body,
|
||||
body,
|
||||
status,
|
||||
metadata,
|
||||
actionCounts,
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
ModerationPhaseContext,
|
||||
} from "talk-server/services/comments/pipeline";
|
||||
|
||||
const context = {
|
||||
comment: { body: "This is a test" },
|
||||
} as ModerationPhaseContext;
|
||||
|
||||
describe("compose", () => {
|
||||
it("handles when a phase throws an error", async () => {
|
||||
const err = new Error("this is an error");
|
||||
@@ -17,14 +21,15 @@ describe("compose", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
await expect(enhanced({} as ModerationPhaseContext)).rejects.toEqual(err);
|
||||
await expect(enhanced(context)).rejects.toEqual(err);
|
||||
});
|
||||
|
||||
it("handles when it returns a status", async () => {
|
||||
const status = GQLCOMMENT_STATUS.ACCEPTED;
|
||||
const enhanced = compose([() => ({ status })]);
|
||||
|
||||
await expect(enhanced({} as ModerationPhaseContext)).resolves.toEqual({
|
||||
await expect(enhanced(context)).resolves.toEqual({
|
||||
body: context.comment.body,
|
||||
status,
|
||||
metadata: {},
|
||||
actions: [],
|
||||
@@ -39,7 +44,8 @@ describe("compose", () => {
|
||||
() => ({ metadata: { third: true } }),
|
||||
]);
|
||||
|
||||
await expect(enhanced({} as ModerationPhaseContext)).resolves.toEqual({
|
||||
await expect(enhanced(context)).resolves.toEqual({
|
||||
body: context.comment.body,
|
||||
status,
|
||||
metadata: { first: true, second: true },
|
||||
actions: [],
|
||||
@@ -81,13 +87,14 @@ describe("compose", () => {
|
||||
}),
|
||||
]);
|
||||
|
||||
const final = await enhanced({} as ModerationPhaseContext);
|
||||
const final = await enhanced(context);
|
||||
|
||||
for (const flag of flags) {
|
||||
expect(final.actions).toContainEqual(flag);
|
||||
}
|
||||
|
||||
expect(final.actions).not.toContainEqual({
|
||||
body: context.comment.body,
|
||||
actionType: ACTION_TYPE.FLAG,
|
||||
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_LINKS,
|
||||
});
|
||||
@@ -99,7 +106,8 @@ describe("compose", () => {
|
||||
() => ({ metadata: { second: true } }),
|
||||
]);
|
||||
|
||||
await expect(enhanced({} as ModerationPhaseContext)).resolves.toEqual({
|
||||
await expect(enhanced(context)).resolves.toEqual({
|
||||
body: context.comment.body,
|
||||
status: GQLCOMMENT_STATUS.NONE,
|
||||
metadata: { first: true, second: true },
|
||||
actions: [],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Omit, Promiseable } from "talk-common/types";
|
||||
import { Omit, Promiseable, RequireProperty } from "talk-common/types";
|
||||
import { GQLCOMMENT_STATUS } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { CreateActionInput } from "talk-server/models/action/comment";
|
||||
import { EditCommentInput } from "talk-server/models/comment";
|
||||
@@ -18,12 +18,13 @@ export interface PhaseResult {
|
||||
actions: ModerationAction[];
|
||||
status: GQLCOMMENT_STATUS;
|
||||
metadata: Record<string, any>;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface ModerationPhaseContext {
|
||||
story: Story;
|
||||
tenant: Tenant;
|
||||
comment: Partial<EditCommentInput>;
|
||||
comment: RequireProperty<Partial<EditCommentInput>, "body">;
|
||||
author: User;
|
||||
req?: Request;
|
||||
}
|
||||
@@ -47,13 +48,21 @@ export const compose = (
|
||||
): ModerationPhase => async context => {
|
||||
const final: PhaseResult = {
|
||||
status: GQLCOMMENT_STATUS.NONE,
|
||||
body: context.comment.body,
|
||||
actions: [],
|
||||
metadata: {},
|
||||
metadata: context.comment.metadata || {},
|
||||
};
|
||||
|
||||
// Loop over all the moderation phases and see if we've resolved the status.
|
||||
for (const phase of phases) {
|
||||
const result = await phase(context);
|
||||
const result = await phase({
|
||||
...context,
|
||||
comment: {
|
||||
...context.comment,
|
||||
body: final.body,
|
||||
metadata: final.metadata,
|
||||
},
|
||||
});
|
||||
if (result) {
|
||||
// If this result contained actions, then we should push it into the
|
||||
// other actions.
|
||||
@@ -72,6 +81,12 @@ export const compose = (
|
||||
};
|
||||
}
|
||||
|
||||
// If the result modified the comment body, we should replace it.
|
||||
const { body } = result;
|
||||
if (body) {
|
||||
final.body = body;
|
||||
}
|
||||
|
||||
// If this result contained a status, then we've finished resolving
|
||||
// phases!
|
||||
const { status } = result;
|
||||
|
||||
@@ -38,7 +38,7 @@ export const commentLength: IntermediateModerationPhase = ({
|
||||
tenant,
|
||||
comment,
|
||||
}): IntermediatePhaseResult | void => {
|
||||
const length = comment.body ? striptags(comment.body).length : 0;
|
||||
const length = striptags(comment.body).length;
|
||||
|
||||
// Reject if the comment is too long or too short.
|
||||
testCharCount(tenant, length);
|
||||
|
||||
+8
-17
@@ -1,38 +1,32 @@
|
||||
import linkify from "linkify-it";
|
||||
import tlds from "tlds";
|
||||
|
||||
import {
|
||||
GQLCOMMENT_FLAG_REASON,
|
||||
GQLCOMMENT_STATUS,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { ACTION_TYPE } from "talk-server/models/action/comment";
|
||||
import { Comment } from "talk-server/models/comment";
|
||||
import { ModerationSettings } from "talk-server/models/settings";
|
||||
import {
|
||||
IntermediateModerationPhase,
|
||||
IntermediatePhaseResult,
|
||||
} from "talk-server/services/comments/pipeline";
|
||||
|
||||
/**
|
||||
* The preloaded linkify instance with common tlds.
|
||||
*/
|
||||
const testForLinks = linkify().tlds(tlds);
|
||||
|
||||
const testPremodLinksEnable = (
|
||||
settings: Partial<ModerationSettings>,
|
||||
body: string
|
||||
) => settings.premodLinksEnable && testForLinks.test(body);
|
||||
comment: Pick<Comment, "metadata">
|
||||
) =>
|
||||
settings.premodLinksEnable && comment.metadata && comment.metadata.linkCount;
|
||||
|
||||
// This phase checks the comment if it has any links in it if the check is
|
||||
// enabled.
|
||||
export const links: IntermediateModerationPhase = ({
|
||||
export const detectLinks: IntermediateModerationPhase = ({
|
||||
story,
|
||||
tenant,
|
||||
comment,
|
||||
}): IntermediatePhaseResult | void => {
|
||||
if (
|
||||
comment.body &&
|
||||
(testPremodLinksEnable(tenant, comment.body) ||
|
||||
(story.settings && testPremodLinksEnable(story.settings, comment.body)))
|
||||
comment &&
|
||||
(testPremodLinksEnable(tenant, comment) ||
|
||||
(story.settings && testPremodLinksEnable(story.settings, comment)))
|
||||
) {
|
||||
// Add the flag related to Trust to the comment.
|
||||
return {
|
||||
@@ -42,9 +36,6 @@ export const links: IntermediateModerationPhase = ({
|
||||
userID: null,
|
||||
actionType: ACTION_TYPE.FLAG,
|
||||
reason: GQLCOMMENT_FLAG_REASON.COMMENT_DETECTED_LINKS,
|
||||
metadata: {
|
||||
links: comment.body,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -2,9 +2,11 @@ import { IntermediateModerationPhase } from "talk-server/services/comments/pipel
|
||||
|
||||
import { commentingDisabled } from "./commentingDisabled";
|
||||
import { commentLength } from "./commentLength";
|
||||
import { detectLinks } from "./detectLinks";
|
||||
import { karma } from "./karma";
|
||||
import { links } from "./links";
|
||||
import { linkify } from "./linkify";
|
||||
import { preModerate } from "./preModerate";
|
||||
import { purify } from "./purify";
|
||||
import { spam } from "./spam";
|
||||
import { staff } from "./staff";
|
||||
import { storyClosed } from "./storyClosed";
|
||||
@@ -18,9 +20,11 @@ export const moderationPhases: IntermediateModerationPhase[] = [
|
||||
commentLength,
|
||||
storyClosed,
|
||||
commentingDisabled,
|
||||
linkify,
|
||||
purify,
|
||||
detectLinks,
|
||||
wordList,
|
||||
staff,
|
||||
links,
|
||||
karma,
|
||||
spam,
|
||||
toxic,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import linkifyjs from "linkifyjs/html";
|
||||
|
||||
import {
|
||||
IntermediateModerationPhase,
|
||||
IntermediatePhaseResult,
|
||||
} from "talk-server/services/comments/pipeline";
|
||||
|
||||
// linkify configuration.
|
||||
const config = {
|
||||
className: "",
|
||||
tagName: "a",
|
||||
};
|
||||
|
||||
export const linkify: IntermediateModerationPhase = async ({
|
||||
comment,
|
||||
}): Promise<IntermediatePhaseResult | void> => ({
|
||||
body: linkifyjs(comment.body, config),
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import { createPurify, sanitizeCommentBody } from "talk-common/utils/purify";
|
||||
import {
|
||||
IntermediateModerationPhase,
|
||||
IntermediatePhaseResult,
|
||||
} from "talk-server/services/comments/pipeline";
|
||||
|
||||
// Initializing JSDOM and DOMPurify
|
||||
const window = new JSDOM("", {}).window;
|
||||
const DOMPurify = createPurify(window);
|
||||
|
||||
export const purify: IntermediateModerationPhase = async ({
|
||||
comment,
|
||||
}): Promise<IntermediatePhaseResult | void> => {
|
||||
const { body, linkCount } = sanitizeCommentBody(DOMPurify, comment.body);
|
||||
|
||||
return {
|
||||
body,
|
||||
metadata: {
|
||||
linkCount,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,35 +1,35 @@
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
import { Comment } from "talk-server/models/comment";
|
||||
import { Story } from "talk-server/models/story";
|
||||
import { Tenant } from "talk-server/models/tenant";
|
||||
import { User } from "talk-server/models/user";
|
||||
import { storyClosed } from "talk-server/services/comments/pipeline/phases/storyClosed";
|
||||
import { ModerationPhaseContext } from "..";
|
||||
|
||||
describe("storyClosed", () => {
|
||||
it("throws an error when the story is closed", () => {
|
||||
expect(() =>
|
||||
storyClosed({
|
||||
story: { closedAt: new Date() } as Story,
|
||||
tenant: {} as Tenant,
|
||||
comment: {} as Comment,
|
||||
author: {} as User,
|
||||
story: { closedAt: new Date() } as ModerationPhaseContext["story"],
|
||||
tenant: {} as ModerationPhaseContext["tenant"],
|
||||
comment: {} as ModerationPhaseContext["comment"],
|
||||
author: {} as ModerationPhaseContext["author"],
|
||||
})
|
||||
).toThrow();
|
||||
|
||||
storyClosed({
|
||||
story: {} as Story,
|
||||
tenant: { autoCloseStream: true } as Tenant,
|
||||
comment: {} as Comment,
|
||||
author: {} as User,
|
||||
story: {} as ModerationPhaseContext["story"],
|
||||
tenant: { autoCloseStream: true } as ModerationPhaseContext["tenant"],
|
||||
comment: {} as ModerationPhaseContext["comment"],
|
||||
author: {} as ModerationPhaseContext["author"],
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
storyClosed({
|
||||
story: { createdAt: new Date() } as Story,
|
||||
tenant: { autoCloseStream: true, closedTimeout: -6000 } as Tenant,
|
||||
comment: {} as Comment,
|
||||
author: {} as User,
|
||||
story: { createdAt: new Date() } as ModerationPhaseContext["story"],
|
||||
tenant: {
|
||||
autoCloseStream: true,
|
||||
closedTimeout: -6000,
|
||||
} as ModerationPhaseContext["tenant"],
|
||||
comment: {} as ModerationPhaseContext["comment"],
|
||||
author: {} as ModerationPhaseContext["author"],
|
||||
})
|
||||
).toThrow();
|
||||
});
|
||||
@@ -43,19 +43,19 @@ describe("storyClosed", () => {
|
||||
closedAt: DateTime.fromJSDate(now)
|
||||
.plus(60000)
|
||||
.toJSDate(),
|
||||
} as Story,
|
||||
tenant: {} as Tenant,
|
||||
comment: {} as Comment,
|
||||
author: {} as User,
|
||||
} as ModerationPhaseContext["story"],
|
||||
tenant: {} as ModerationPhaseContext["tenant"],
|
||||
comment: {} as ModerationPhaseContext["comment"],
|
||||
author: {} as ModerationPhaseContext["author"],
|
||||
})
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
storyClosed({
|
||||
story: {} as Story,
|
||||
tenant: {} as Tenant,
|
||||
comment: {} as Comment,
|
||||
author: {} as User,
|
||||
story: {} as ModerationPhaseContext["story"],
|
||||
tenant: {} as ModerationPhaseContext["tenant"],
|
||||
comment: {} as ModerationPhaseContext["comment"],
|
||||
author: {} as ModerationPhaseContext["author"],
|
||||
})
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
declare module "dompurify" {
|
||||
interface Config<T extends boolean> {
|
||||
ALLOWED_ATTR?: string[];
|
||||
ALLOWED_TAGS?: string[];
|
||||
RETURN_DOM?: boolean;
|
||||
RETURN_DOM_FRAGMENT?: T;
|
||||
}
|
||||
|
||||
class DOMPurify<T extends boolean = true> {
|
||||
public setConfig(config: Config<T>): void;
|
||||
public sanitize(source: string): T extends true ? HTMLBodyElement : string;
|
||||
public addHook(name: string, callback: (node: Element) => void): void;
|
||||
}
|
||||
|
||||
export default function createDOMPurify<T extends boolean>(
|
||||
window: any
|
||||
): DOMPurify<T>;
|
||||
}
|
||||
Reference in New Issue
Block a user