mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-07 08:34:10 +01:00
Add:HTML sanitizer lib to support html in podcasts and replace strip html lib
This commit is contained in:
parent
96232676cb
commit
c4bfa266b0
360
package-lock.json
generated
360
package-lock.json
generated
@ -1,12 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.0.14",
|
"version": "2.0.17",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf",
|
"version": "2.0.17",
|
||||||
"version": "2.0.14",
|
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "^5.3.0",
|
"archiver": "^5.3.0",
|
||||||
@ -20,6 +19,7 @@
|
|||||||
"fast-sort": "^3.1.1",
|
"fast-sort": "^3.1.1",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
|
"htmlparser2": "^8.0.1",
|
||||||
"image-type": "^4.1.0",
|
"image-type": "^4.1.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"libgen": "^2.1.0",
|
"libgen": "^2.1.0",
|
||||||
@ -31,24 +31,11 @@
|
|||||||
"read-chunk": "^3.1.0",
|
"read-chunk": "^3.1.0",
|
||||||
"recursive-readdir-async": "^1.1.8",
|
"recursive-readdir-async": "^1.1.8",
|
||||||
"socket.io": "^4.4.1",
|
"socket.io": "^4.4.1",
|
||||||
"string-strip-html": "^8.3.0",
|
|
||||||
"watcher": "^1.2.0",
|
"watcher": "^1.2.0",
|
||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"audiobookshelf": "prod.js"
|
"audiobookshelf": "prod.js"
|
||||||
},
|
|
||||||
"devDependencies": {}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/runtime": {
|
|
||||||
"version": "7.17.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz",
|
|
||||||
"integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==",
|
|
||||||
"dependencies": {
|
|
||||||
"regenerator-runtime": "^0.13.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sindresorhus/is": {
|
"node_modules/@sindresorhus/is": {
|
||||||
@ -625,6 +612,57 @@
|
|||||||
"node": ">=4.5.0"
|
"node": ">=4.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-serializer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"entities": "^4.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/domelementtype": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/domhandler": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/domutils": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"dom-serializer": "^2.0.0",
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ecdsa-sig-formatter": {
|
"node_modules/ecdsa-sig-formatter": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||||
@ -711,6 +749,17 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-html": {
|
"node_modules/escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
@ -994,10 +1043,23 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/html-entities": {
|
"node_modules/htmlparser2": {
|
||||||
"version": "2.3.3",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
|
||||||
"integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
|
"integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"domutils": "^3.0.1",
|
||||||
|
"entities": "^4.3.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-cache-semantics": {
|
"node_modules/http-cache-semantics": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
@ -1225,11 +1287,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
|
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.clonedeep": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
|
||||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.defaults": {
|
"node_modules/lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
@ -1280,21 +1337,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||||
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
|
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.trim": {
|
|
||||||
"version": "4.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
|
||||||
"integrity": "sha1-NkJefukL5KpeJ7zruFt9EepHqlc="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.union": {
|
"node_modules/lodash.union": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||||
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
|
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.without": {
|
|
||||||
"version": "4.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
|
|
||||||
"integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw="
|
|
||||||
},
|
|
||||||
"node_modules/lowercase-keys": {
|
"node_modules/lowercase-keys": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||||
@ -1625,44 +1672,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ranges-apply": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-apply/-/ranges-apply-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-VF3a0XUuYS/BQHv2RaIyX1K7S1hbfrs64hkGKgPVk0Y7p4XFwSucjTTttrBqmkcmB/PZx5ISTZdxErRZi/89aQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"ranges-merge": "^7.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ranges-merge": {
|
|
||||||
"version": "7.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-merge/-/ranges-merge-7.1.0.tgz",
|
|
||||||
"integrity": "sha512-coTHcyAEIhoEdsBs9f5f+q0rmy7UHvS/5nfuXzuj5oLX/l/tbqM5uxRb6eh8WMdetXia3lK67ZO4tarH4ieulQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"ranges-push": "^5.1.0",
|
|
||||||
"ranges-sort": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ranges-push": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-push/-/ranges-push-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-vqGcaGq7GWV1zBa9w83E+dzYkOvE9/3pIRUPvLf12c+mGQCf1nesrkBI7Ob8taN2CC9V1HDSJx0KAQl0SgZftA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"ranges-merge": "^7.1.0",
|
|
||||||
"string-collapse-leading-whitespace": "^5.1.0",
|
|
||||||
"string-trim-spaces-only": "^3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ranges-sort": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-sort/-/ranges-sort-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-GOQgk6UtsrfKFeYa53YLiBVnLINwYmOk5l2QZG1csZpT6GdImUwooh+/cRrp7b+fYawZX/rnyA3Ul+pdgQBIzA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/raw-body": {
|
"node_modules/raw-body": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||||
@ -1718,11 +1727,6 @@
|
|||||||
"node": ">=7.6"
|
"node": ">=7.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
|
||||||
"version": "0.13.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
|
||||||
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
|
|
||||||
},
|
|
||||||
"node_modules/resolve-alpn": {
|
"node_modules/resolve-alpn": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||||
@ -1987,52 +1991,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
},
|
},
|
||||||
"node_modules/string-collapse-leading-whitespace": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-collapse-leading-whitespace/-/string-collapse-leading-whitespace-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-mYz9/Kb5uvRB4DZj46zILwI4y9lD9JsvXG9Xb7zjbwm0I/R40G7oFfMsqJ28l2d7gWMTLJL569NfJQVLQbnHCw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-indexes": {
|
"node_modules/string-indexes": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz",
|
||||||
"integrity": "sha512-RUlx+2YydZJNlRAvoh1siPYWj/Xfk6t1sQLkA5n1tMGRCKkRLzkRtJhHk4qRmKergEBh8R3pWhsUsDqia/bolw=="
|
"integrity": "sha512-RUlx+2YydZJNlRAvoh1siPYWj/Xfk6t1sQLkA5n1tMGRCKkRLzkRtJhHk4qRmKergEBh8R3pWhsUsDqia/bolw=="
|
||||||
},
|
},
|
||||||
"node_modules/string-left-right": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-left-right/-/string-left-right-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-ic/WvfNVUygWWsgg8akzSzp2NuttfhrdbH7QmSnda5b5RFmT9aCEDiS/M+gmTJwtFy7+b/2AXU4Z6vejcePQqQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"lodash.clonedeep": "^4.5.0",
|
|
||||||
"lodash.isplainobject": "^4.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-strip-html": {
|
|
||||||
"version": "8.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-strip-html/-/string-strip-html-8.3.0.tgz",
|
|
||||||
"integrity": "sha512-1+rjTPt0JjpFr1w0bfNL1S6O0I9fJDqM+P3pFTpC6eEEpIXhmBvPLnaQoEuWarswiH219qCefDSxTLxGQyHKUg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"html-entities": "^2.3.2",
|
|
||||||
"lodash.isplainobject": "^4.0.6",
|
|
||||||
"lodash.trim": "^4.5.1",
|
|
||||||
"lodash.without": "^4.4.0",
|
|
||||||
"ranges-apply": "^5.1.0",
|
|
||||||
"ranges-push": "^5.1.0",
|
|
||||||
"string-left-right": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-trim-spaces-only": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-trim-spaces-only/-/string-trim-spaces-only-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-AW7RSi3+QtE6wR+4m/kmwlyy39neBbCIzrzzu1/RGzNRiPKQOeB3rGzr4ubg4UIQgYtr2w0PrxhKPXgyqJ0vaQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tar-stream": {
|
"node_modules/tar-stream": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||||
@ -2229,14 +2192,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
|
||||||
"version": "7.17.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz",
|
|
||||||
"integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==",
|
|
||||||
"requires": {
|
|
||||||
"regenerator-runtime": "^0.13.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sindresorhus/is": {
|
"@sindresorhus/is": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||||
@ -2683,6 +2638,39 @@
|
|||||||
"streamsearch": "0.1.2"
|
"streamsearch": "0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dom-serializer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"entities": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domelementtype": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
|
||||||
|
},
|
||||||
|
"domhandler": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domutils": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
|
||||||
|
"requires": {
|
||||||
|
"dom-serializer": "^2.0.0",
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ecdsa-sig-formatter": {
|
"ecdsa-sig-formatter": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||||
@ -2751,6 +2739,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
|
||||||
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg=="
|
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg=="
|
||||||
},
|
},
|
||||||
|
"entities": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg=="
|
||||||
|
},
|
||||||
"escape-html": {
|
"escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
@ -2960,10 +2953,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||||
},
|
},
|
||||||
"html-entities": {
|
"htmlparser2": {
|
||||||
"version": "2.3.3",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
|
||||||
"integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
|
"integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"domutils": "^3.0.1",
|
||||||
|
"entities": "^4.3.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"http-cache-semantics": {
|
"http-cache-semantics": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
@ -3154,11 +3153,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
|
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
|
||||||
},
|
},
|
||||||
"lodash.clonedeep": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
|
||||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
|
||||||
},
|
|
||||||
"lodash.defaults": {
|
"lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
@ -3209,21 +3203,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||||
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
|
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
|
||||||
},
|
},
|
||||||
"lodash.trim": {
|
|
||||||
"version": "4.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
|
||||||
"integrity": "sha1-NkJefukL5KpeJ7zruFt9EepHqlc="
|
|
||||||
},
|
|
||||||
"lodash.union": {
|
"lodash.union": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||||
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
|
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
|
||||||
},
|
},
|
||||||
"lodash.without": {
|
|
||||||
"version": "4.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
|
|
||||||
"integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw="
|
|
||||||
},
|
|
||||||
"lowercase-keys": {
|
"lowercase-keys": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||||
@ -3451,44 +3435,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
},
|
},
|
||||||
"ranges-apply": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-apply/-/ranges-apply-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-VF3a0XUuYS/BQHv2RaIyX1K7S1hbfrs64hkGKgPVk0Y7p4XFwSucjTTttrBqmkcmB/PZx5ISTZdxErRZi/89aQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"ranges-merge": "^7.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ranges-merge": {
|
|
||||||
"version": "7.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-merge/-/ranges-merge-7.1.0.tgz",
|
|
||||||
"integrity": "sha512-coTHcyAEIhoEdsBs9f5f+q0rmy7UHvS/5nfuXzuj5oLX/l/tbqM5uxRb6eh8WMdetXia3lK67ZO4tarH4ieulQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"ranges-push": "^5.1.0",
|
|
||||||
"ranges-sort": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ranges-push": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-push/-/ranges-push-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-vqGcaGq7GWV1zBa9w83E+dzYkOvE9/3pIRUPvLf12c+mGQCf1nesrkBI7Ob8taN2CC9V1HDSJx0KAQl0SgZftA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"ranges-merge": "^7.1.0",
|
|
||||||
"string-collapse-leading-whitespace": "^5.1.0",
|
|
||||||
"string-trim-spaces-only": "^3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ranges-sort": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ranges-sort/-/ranges-sort-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-GOQgk6UtsrfKFeYa53YLiBVnLINwYmOk5l2QZG1csZpT6GdImUwooh+/cRrp7b+fYawZX/rnyA3Ul+pdgQBIzA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"raw-body": {
|
"raw-body": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||||
@ -3532,11 +3478,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/recursive-readdir-async/-/recursive-readdir-async-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/recursive-readdir-async/-/recursive-readdir-async-1.2.1.tgz",
|
||||||
"integrity": "sha512-fU8aySmHIhrycTlXn+hI7dS/p7GnrMHzr2xDdBSd8HZ16mbLkmfIEccIE80gLHftrkTt9oDJiGEJNIPY6n0v6A=="
|
"integrity": "sha512-fU8aySmHIhrycTlXn+hI7dS/p7GnrMHzr2xDdBSd8HZ16mbLkmfIEccIE80gLHftrkTt9oDJiGEJNIPY6n0v6A=="
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
|
||||||
"version": "0.13.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
|
||||||
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
|
|
||||||
},
|
|
||||||
"resolve-alpn": {
|
"resolve-alpn": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||||
@ -3748,52 +3689,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string-collapse-leading-whitespace": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-collapse-leading-whitespace/-/string-collapse-leading-whitespace-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-mYz9/Kb5uvRB4DZj46zILwI4y9lD9JsvXG9Xb7zjbwm0I/R40G7oFfMsqJ28l2d7gWMTLJL569NfJQVLQbnHCw==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string-indexes": {
|
"string-indexes": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz",
|
||||||
"integrity": "sha512-RUlx+2YydZJNlRAvoh1siPYWj/Xfk6t1sQLkA5n1tMGRCKkRLzkRtJhHk4qRmKergEBh8R3pWhsUsDqia/bolw=="
|
"integrity": "sha512-RUlx+2YydZJNlRAvoh1siPYWj/Xfk6t1sQLkA5n1tMGRCKkRLzkRtJhHk4qRmKergEBh8R3pWhsUsDqia/bolw=="
|
||||||
},
|
},
|
||||||
"string-left-right": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-left-right/-/string-left-right-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-ic/WvfNVUygWWsgg8akzSzp2NuttfhrdbH7QmSnda5b5RFmT9aCEDiS/M+gmTJwtFy7+b/2AXU4Z6vejcePQqQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"lodash.clonedeep": "^4.5.0",
|
|
||||||
"lodash.isplainobject": "^4.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string-strip-html": {
|
|
||||||
"version": "8.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-strip-html/-/string-strip-html-8.3.0.tgz",
|
|
||||||
"integrity": "sha512-1+rjTPt0JjpFr1w0bfNL1S6O0I9fJDqM+P3pFTpC6eEEpIXhmBvPLnaQoEuWarswiH219qCefDSxTLxGQyHKUg==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0",
|
|
||||||
"html-entities": "^2.3.2",
|
|
||||||
"lodash.isplainobject": "^4.0.6",
|
|
||||||
"lodash.trim": "^4.5.1",
|
|
||||||
"lodash.without": "^4.4.0",
|
|
||||||
"ranges-apply": "^5.1.0",
|
|
||||||
"ranges-push": "^5.1.0",
|
|
||||||
"string-left-right": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string-trim-spaces-only": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-trim-spaces-only/-/string-trim-spaces-only-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-AW7RSi3+QtE6wR+4m/kmwlyy39neBbCIzrzzu1/RGzNRiPKQOeB3rGzr4ubg4UIQgYtr2w0PrxhKPXgyqJ0vaQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tar-stream": {
|
"tar-stream": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"fast-sort": "^3.1.1",
|
"fast-sort": "^3.1.1",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
|
"htmlparser2": "^8.0.1",
|
||||||
"image-type": "^4.1.0",
|
"image-type": "^4.1.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"libgen": "^2.1.0",
|
"libgen": "^2.1.0",
|
||||||
@ -49,8 +50,7 @@
|
|||||||
"read-chunk": "^3.1.0",
|
"read-chunk": "^3.1.0",
|
||||||
"recursive-readdir-async": "^1.1.8",
|
"recursive-readdir-async": "^1.1.8",
|
||||||
"socket.io": "^4.4.1",
|
"socket.io": "^4.4.1",
|
||||||
"string-strip-html": "^8.3.0",
|
|
||||||
"watcher": "^1.2.0",
|
"watcher": "^1.2.0",
|
||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
874
server/libs/sanitizeHtml.js
Normal file
874
server/libs/sanitizeHtml.js
Normal file
@ -0,0 +1,874 @@
|
|||||||
|
/*
|
||||||
|
sanitize-html (Apostrophe Technologies)
|
||||||
|
SOURCE: https://github.com/apostrophecms/sanitize-html
|
||||||
|
LICENSE: https://github.com/apostrophecms/sanitize-html/blob/main/LICENSE
|
||||||
|
|
||||||
|
Modified for audiobookshelf
|
||||||
|
*/
|
||||||
|
|
||||||
|
const htmlparser = require('htmlparser2');
|
||||||
|
// const escapeStringRegexp = require('escape-string-regexp');
|
||||||
|
// const { isPlainObject } = require('is-plain-object');
|
||||||
|
// const deepmerge = require('deepmerge');
|
||||||
|
// const parseSrcset = require('parse-srcset');
|
||||||
|
// const { parse: postcssParse } = require('postcss');
|
||||||
|
// Tags that can conceivably represent stand-alone media.
|
||||||
|
|
||||||
|
// ABS UPDATE: Packages not necessary
|
||||||
|
// SOURCE: https://github.com/sindresorhus/escape-string-regexp/blob/main/index.js
|
||||||
|
function escapeStringRegexp(string) {
|
||||||
|
if (typeof string !== 'string') {
|
||||||
|
throw new TypeError('Expected a string');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape characters with special meaning either inside or outside character sets.
|
||||||
|
// Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
|
||||||
|
return string
|
||||||
|
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
|
||||||
|
.replace(/-/g, '\\x2d');
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOURCE: https://github.com/jonschlinkert/is-plain-object/blob/master/is-plain-object.js
|
||||||
|
function isObject(o) {
|
||||||
|
return Object.prototype.toString.call(o) === '[object Object]';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPlainObject(o) {
|
||||||
|
var ctor, prot;
|
||||||
|
|
||||||
|
if (isObject(o) === false) return false;
|
||||||
|
|
||||||
|
// If has modified constructor
|
||||||
|
ctor = o.constructor;
|
||||||
|
if (ctor === undefined) return true;
|
||||||
|
|
||||||
|
// If has modified prototype
|
||||||
|
prot = ctor.prototype;
|
||||||
|
if (isObject(prot) === false) return false;
|
||||||
|
|
||||||
|
// If constructor does not have an Object-specific method
|
||||||
|
if (prot.hasOwnProperty('isPrototypeOf') === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most likely a plain Object
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const mediaTags = [
|
||||||
|
'img', 'audio', 'video', 'picture', 'svg',
|
||||||
|
'object', 'map', 'iframe', 'embed'
|
||||||
|
];
|
||||||
|
// Tags that are inherently vulnerable to being used in XSS attacks.
|
||||||
|
const vulnerableTags = ['script', 'style'];
|
||||||
|
|
||||||
|
function each(obj, cb) {
|
||||||
|
if (obj) {
|
||||||
|
Object.keys(obj).forEach(function (key) {
|
||||||
|
cb(obj[key], key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid false positives with .__proto__, .hasOwnProperty, etc.
|
||||||
|
function has(obj, key) {
|
||||||
|
return ({}).hasOwnProperty.call(obj, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns those elements of `a` for which `cb(a)` returns truthy
|
||||||
|
function filter(a, cb) {
|
||||||
|
const n = [];
|
||||||
|
each(a, function (v) {
|
||||||
|
if (cb(v)) {
|
||||||
|
n.push(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmptyObject(obj) {
|
||||||
|
for (const key in obj) {
|
||||||
|
if (has(obj, key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifySrcset(parsedSrcset) {
|
||||||
|
return parsedSrcset.map(function (part) {
|
||||||
|
if (!part.url) {
|
||||||
|
throw new Error('URL missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
part.url +
|
||||||
|
(part.w ? ` ${part.w}w` : '') +
|
||||||
|
(part.h ? ` ${part.h}h` : '') +
|
||||||
|
(part.d ? ` ${part.d}x` : '')
|
||||||
|
);
|
||||||
|
}).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = sanitizeHtml;
|
||||||
|
|
||||||
|
// A valid attribute name.
|
||||||
|
// We use a tolerant definition based on the set of strings defined by
|
||||||
|
// html.spec.whatwg.org/multipage/parsing.html#before-attribute-name-state
|
||||||
|
// and html.spec.whatwg.org/multipage/parsing.html#attribute-name-state .
|
||||||
|
// The characters accepted are ones which can be appended to the attribute
|
||||||
|
// name buffer without triggering a parse error:
|
||||||
|
// * unexpected-equals-sign-before-attribute-name
|
||||||
|
// * unexpected-null-character
|
||||||
|
// * unexpected-character-in-attribute-name
|
||||||
|
// We exclude the empty string because it's impossible to get to the after
|
||||||
|
// attribute name state with an empty attribute name buffer.
|
||||||
|
const VALID_HTML_ATTRIBUTE_NAME = /^[^\0\t\n\f\r /<=>]+$/;
|
||||||
|
|
||||||
|
// Ignore the _recursing flag; it's there for recursive
|
||||||
|
// invocation as a guard against this exploit:
|
||||||
|
// https://github.com/fb55/htmlparser2/issues/105
|
||||||
|
|
||||||
|
function sanitizeHtml(html, options, _recursing) {
|
||||||
|
if (html == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
// Used for hot swapping the result variable with an empty string in order to "capture" the text written to it.
|
||||||
|
let tempResult = '';
|
||||||
|
|
||||||
|
function Frame(tag, attribs) {
|
||||||
|
const that = this;
|
||||||
|
this.tag = tag;
|
||||||
|
this.attribs = attribs || {};
|
||||||
|
this.tagPosition = result.length;
|
||||||
|
this.text = ''; // Node inner text
|
||||||
|
this.mediaChildren = [];
|
||||||
|
|
||||||
|
this.updateParentNodeText = function () {
|
||||||
|
if (stack.length) {
|
||||||
|
const parentFrame = stack[stack.length - 1];
|
||||||
|
parentFrame.text += that.text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.updateParentNodeMediaChildren = function () {
|
||||||
|
if (stack.length && mediaTags.includes(this.tag)) {
|
||||||
|
const parentFrame = stack[stack.length - 1];
|
||||||
|
parentFrame.mediaChildren.push(this.tag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
options = Object.assign({}, sanitizeHtml.defaults, options);
|
||||||
|
options.parser = Object.assign({}, htmlParserDefaults, options.parser);
|
||||||
|
|
||||||
|
// vulnerableTags
|
||||||
|
vulnerableTags.forEach(function (tag) {
|
||||||
|
if (
|
||||||
|
options.allowedTags && options.allowedTags.indexOf(tag) > -1 &&
|
||||||
|
!options.allowVulnerableTags
|
||||||
|
) {
|
||||||
|
console.warn(`\n\n⚠️ Your \`allowedTags\` option includes, \`${tag}\`, which is inherently\nvulnerable to XSS attacks. Please remove it from \`allowedTags\`.\nOr, to disable this warning, add the \`allowVulnerableTags\` option\nand ensure you are accounting for this risk.\n\n`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tags that contain something other than HTML, or where discarding
|
||||||
|
// the text when the tag is disallowed makes sense for other reasons.
|
||||||
|
// If we are not allowing these tags, we should drop their content too.
|
||||||
|
// For other tags you would drop the tag but keep its content.
|
||||||
|
const nonTextTagsArray = options.nonTextTags || [
|
||||||
|
'script',
|
||||||
|
'style',
|
||||||
|
'textarea',
|
||||||
|
'option'
|
||||||
|
];
|
||||||
|
let allowedAttributesMap;
|
||||||
|
let allowedAttributesGlobMap;
|
||||||
|
if (options.allowedAttributes) {
|
||||||
|
allowedAttributesMap = {};
|
||||||
|
allowedAttributesGlobMap = {};
|
||||||
|
each(options.allowedAttributes, function (attributes, tag) {
|
||||||
|
allowedAttributesMap[tag] = [];
|
||||||
|
const globRegex = [];
|
||||||
|
attributes.forEach(function (obj) {
|
||||||
|
if (typeof obj === 'string' && obj.indexOf('*') >= 0) {
|
||||||
|
globRegex.push(escapeStringRegexp(obj).replace(/\\\*/g, '.*'));
|
||||||
|
} else {
|
||||||
|
allowedAttributesMap[tag].push(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (globRegex.length) {
|
||||||
|
allowedAttributesGlobMap[tag] = new RegExp('^(' + globRegex.join('|') + ')$');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const allowedClassesMap = {};
|
||||||
|
const allowedClassesGlobMap = {};
|
||||||
|
const allowedClassesRegexMap = {};
|
||||||
|
each(options.allowedClasses, function (classes, tag) {
|
||||||
|
// Implicitly allows the class attribute
|
||||||
|
if (allowedAttributesMap) {
|
||||||
|
if (!has(allowedAttributesMap, tag)) {
|
||||||
|
allowedAttributesMap[tag] = [];
|
||||||
|
}
|
||||||
|
allowedAttributesMap[tag].push('class');
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedClassesMap[tag] = [];
|
||||||
|
allowedClassesRegexMap[tag] = [];
|
||||||
|
const globRegex = [];
|
||||||
|
classes.forEach(function (obj) {
|
||||||
|
if (typeof obj === 'string' && obj.indexOf('*') >= 0) {
|
||||||
|
globRegex.push(escapeStringRegexp(obj).replace(/\\\*/g, '.*'));
|
||||||
|
} else if (obj instanceof RegExp) {
|
||||||
|
allowedClassesRegexMap[tag].push(obj);
|
||||||
|
} else {
|
||||||
|
allowedClassesMap[tag].push(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (globRegex.length) {
|
||||||
|
allowedClassesGlobMap[tag] = new RegExp('^(' + globRegex.join('|') + ')$');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const transformTagsMap = {};
|
||||||
|
let transformTagsAll;
|
||||||
|
each(options.transformTags, function (transform, tag) {
|
||||||
|
let transFun;
|
||||||
|
if (typeof transform === 'function') {
|
||||||
|
transFun = transform;
|
||||||
|
} else if (typeof transform === 'string') {
|
||||||
|
transFun = sanitizeHtml.simpleTransform(transform);
|
||||||
|
}
|
||||||
|
if (tag === '*') {
|
||||||
|
transformTagsAll = transFun;
|
||||||
|
} else {
|
||||||
|
transformTagsMap[tag] = transFun;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let depth;
|
||||||
|
let stack;
|
||||||
|
let skipMap;
|
||||||
|
let transformMap;
|
||||||
|
let skipText;
|
||||||
|
let skipTextDepth;
|
||||||
|
let addedText = false;
|
||||||
|
|
||||||
|
initializeState();
|
||||||
|
|
||||||
|
const parser = new htmlparser.Parser({
|
||||||
|
onopentag: function (name, attribs) {
|
||||||
|
// If `enforceHtmlBoundary` is `true` and this has found the opening
|
||||||
|
// `html` tag, reset the state.
|
||||||
|
if (options.enforceHtmlBoundary && name === 'html') {
|
||||||
|
initializeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipText) {
|
||||||
|
skipTextDepth++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const frame = new Frame(name, attribs);
|
||||||
|
stack.push(frame);
|
||||||
|
|
||||||
|
let skip = false;
|
||||||
|
const hasText = !!frame.text;
|
||||||
|
let transformedTag;
|
||||||
|
if (has(transformTagsMap, name)) {
|
||||||
|
transformedTag = transformTagsMap[name](name, attribs);
|
||||||
|
|
||||||
|
frame.attribs = attribs = transformedTag.attribs;
|
||||||
|
|
||||||
|
if (transformedTag.text !== undefined) {
|
||||||
|
frame.innerText = transformedTag.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name !== transformedTag.tagName) {
|
||||||
|
frame.name = name = transformedTag.tagName;
|
||||||
|
transformMap[depth] = transformedTag.tagName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (transformTagsAll) {
|
||||||
|
transformedTag = transformTagsAll(name, attribs);
|
||||||
|
|
||||||
|
frame.attribs = attribs = transformedTag.attribs;
|
||||||
|
if (name !== transformedTag.tagName) {
|
||||||
|
frame.name = name = transformedTag.tagName;
|
||||||
|
transformMap[depth] = transformedTag.tagName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((options.allowedTags && options.allowedTags.indexOf(name) === -1) || (options.disallowedTagsMode === 'recursiveEscape' && !isEmptyObject(skipMap)) || (options.nestingLimit != null && depth >= options.nestingLimit)) {
|
||||||
|
skip = true;
|
||||||
|
skipMap[depth] = true;
|
||||||
|
if (options.disallowedTagsMode === 'discard') {
|
||||||
|
if (nonTextTagsArray.indexOf(name) !== -1) {
|
||||||
|
skipText = true;
|
||||||
|
skipTextDepth = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skipMap[depth] = true;
|
||||||
|
}
|
||||||
|
depth++;
|
||||||
|
if (skip) {
|
||||||
|
if (options.disallowedTagsMode === 'discard') {
|
||||||
|
// We want the contents but not this tag
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tempResult = result;
|
||||||
|
result = '';
|
||||||
|
}
|
||||||
|
result += '<' + name;
|
||||||
|
|
||||||
|
if (name === 'script') {
|
||||||
|
if (options.allowedScriptHostnames || options.allowedScriptDomains) {
|
||||||
|
frame.innerText = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowedAttributesMap || has(allowedAttributesMap, name) || allowedAttributesMap['*']) {
|
||||||
|
each(attribs, function (value, a) {
|
||||||
|
if (!VALID_HTML_ATTRIBUTE_NAME.test(a)) {
|
||||||
|
// This prevents part of an attribute name in the output from being
|
||||||
|
// interpreted as the end of an attribute, or end of a tag.
|
||||||
|
delete frame.attribs[a];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let parsed;
|
||||||
|
// check allowedAttributesMap for the element and attribute and modify the value
|
||||||
|
// as necessary if there are specific values defined.
|
||||||
|
let passedAllowedAttributesMapCheck = false;
|
||||||
|
if (!allowedAttributesMap ||
|
||||||
|
(has(allowedAttributesMap, name) && allowedAttributesMap[name].indexOf(a) !== -1) ||
|
||||||
|
(allowedAttributesMap['*'] && allowedAttributesMap['*'].indexOf(a) !== -1) ||
|
||||||
|
(has(allowedAttributesGlobMap, name) && allowedAttributesGlobMap[name].test(a)) ||
|
||||||
|
(allowedAttributesGlobMap['*'] && allowedAttributesGlobMap['*'].test(a))) {
|
||||||
|
passedAllowedAttributesMapCheck = true;
|
||||||
|
} else if (allowedAttributesMap && allowedAttributesMap[name]) {
|
||||||
|
for (const o of allowedAttributesMap[name]) {
|
||||||
|
if (isPlainObject(o) && o.name && (o.name === a)) {
|
||||||
|
passedAllowedAttributesMapCheck = true;
|
||||||
|
let newValue = '';
|
||||||
|
if (o.multiple === true) {
|
||||||
|
// verify the values that are allowed
|
||||||
|
const splitStrArray = value.split(' ');
|
||||||
|
for (const s of splitStrArray) {
|
||||||
|
if (o.values.indexOf(s) !== -1) {
|
||||||
|
if (newValue === '') {
|
||||||
|
newValue = s;
|
||||||
|
} else {
|
||||||
|
newValue += ' ' + s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (o.values.indexOf(value) >= 0) {
|
||||||
|
// verified an allowed value matches the entire attribute value
|
||||||
|
newValue = value;
|
||||||
|
}
|
||||||
|
value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (passedAllowedAttributesMapCheck) {
|
||||||
|
if (options.allowedSchemesAppliedToAttributes.indexOf(a) !== -1) {
|
||||||
|
if (naughtyHref(name, value)) {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'script' && a === 'src') {
|
||||||
|
|
||||||
|
let allowed = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = new URL(value);
|
||||||
|
|
||||||
|
if (options.allowedScriptHostnames || options.allowedScriptDomains) {
|
||||||
|
const allowedHostname = (options.allowedScriptHostnames || []).find(function (hostname) {
|
||||||
|
return hostname === parsed.hostname;
|
||||||
|
});
|
||||||
|
const allowedDomain = (options.allowedScriptDomains || []).find(function (domain) {
|
||||||
|
return parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`);
|
||||||
|
});
|
||||||
|
allowed = allowedHostname || allowedDomain;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowed) {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'iframe' && a === 'src') {
|
||||||
|
let allowed = true;
|
||||||
|
try {
|
||||||
|
// Chrome accepts \ as a substitute for / in the // at the
|
||||||
|
// start of a URL, so rewrite accordingly to prevent exploit.
|
||||||
|
// Also drop any whitespace at that point in the URL
|
||||||
|
value = value.replace(/^(\w+:)?\s*[\\/]\s*[\\/]/, '$1//');
|
||||||
|
if (value.startsWith('relative:')) {
|
||||||
|
// An attempt to exploit our workaround for base URLs being
|
||||||
|
// mandatory for relative URL validation in the WHATWG
|
||||||
|
// URL parser, reject it
|
||||||
|
throw new Error('relative: exploit attempt');
|
||||||
|
}
|
||||||
|
// naughtyHref is in charge of whether protocol relative URLs
|
||||||
|
// are cool. Here we are concerned just with allowed hostnames and
|
||||||
|
// whether to allow relative URLs.
|
||||||
|
//
|
||||||
|
// Build a placeholder "base URL" against which any reasonable
|
||||||
|
// relative URL may be parsed successfully
|
||||||
|
let base = 'relative://relative-site';
|
||||||
|
for (let i = 0; (i < 100); i++) {
|
||||||
|
base += `/${i}`;
|
||||||
|
}
|
||||||
|
const parsed = new URL(value, base);
|
||||||
|
const isRelativeUrl = parsed && parsed.hostname === 'relative-site' && parsed.protocol === 'relative:';
|
||||||
|
if (isRelativeUrl) {
|
||||||
|
// default value of allowIframeRelativeUrls is true
|
||||||
|
// unless allowedIframeHostnames or allowedIframeDomains specified
|
||||||
|
allowed = has(options, 'allowIframeRelativeUrls')
|
||||||
|
? options.allowIframeRelativeUrls
|
||||||
|
: (!options.allowedIframeHostnames && !options.allowedIframeDomains);
|
||||||
|
} else if (options.allowedIframeHostnames || options.allowedIframeDomains) {
|
||||||
|
const allowedHostname = (options.allowedIframeHostnames || []).find(function (hostname) {
|
||||||
|
return hostname === parsed.hostname;
|
||||||
|
});
|
||||||
|
const allowedDomain = (options.allowedIframeDomains || []).find(function (domain) {
|
||||||
|
return parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`);
|
||||||
|
});
|
||||||
|
allowed = allowedHostname || allowedDomain;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Unparseable iframe src
|
||||||
|
allowed = false;
|
||||||
|
}
|
||||||
|
if (!allowed) {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a === 'srcset') {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
|
||||||
|
// ABS UPDATE: srcset not necessary
|
||||||
|
// try {
|
||||||
|
// parsed = parseSrcset(value);
|
||||||
|
// parsed.forEach(function (value) {
|
||||||
|
// if (naughtyHref('srcset', value.url)) {
|
||||||
|
// value.evil = true;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// parsed = filter(parsed, function (v) {
|
||||||
|
// return !v.evil;
|
||||||
|
// });
|
||||||
|
// if (!parsed.length) {
|
||||||
|
// delete frame.attribs[a];
|
||||||
|
// return;
|
||||||
|
// } else {
|
||||||
|
// value = stringifySrcset(filter(parsed, function (v) {
|
||||||
|
// return !v.evil;
|
||||||
|
// }));
|
||||||
|
// frame.attribs[a] = value;
|
||||||
|
// }
|
||||||
|
// } catch (e) {
|
||||||
|
// // Unparseable srcset
|
||||||
|
// delete frame.attribs[a];
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if (a === 'class') {
|
||||||
|
const allowedSpecificClasses = allowedClassesMap[name];
|
||||||
|
const allowedWildcardClasses = allowedClassesMap['*'];
|
||||||
|
const allowedSpecificClassesGlob = allowedClassesGlobMap[name];
|
||||||
|
const allowedSpecificClassesRegex = allowedClassesRegexMap[name];
|
||||||
|
const allowedWildcardClassesGlob = allowedClassesGlobMap['*'];
|
||||||
|
const allowedClassesGlobs = [
|
||||||
|
allowedSpecificClassesGlob,
|
||||||
|
allowedWildcardClassesGlob
|
||||||
|
]
|
||||||
|
.concat(allowedSpecificClassesRegex)
|
||||||
|
.filter(function (t) {
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
if (allowedSpecificClasses && allowedWildcardClasses) {
|
||||||
|
// ABS UPDATE: classes and wildcard classes not necessary now
|
||||||
|
// value = filterClasses(value, deepmerge(allowedSpecificClasses, allowedWildcardClasses), allowedClassesGlobs);
|
||||||
|
} else {
|
||||||
|
value = filterClasses(value, allowedSpecificClasses || allowedWildcardClasses, allowedClassesGlobs);
|
||||||
|
}
|
||||||
|
if (!value.length) {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a === 'style') {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
|
||||||
|
// ABS UPDATE: Styles not necessary
|
||||||
|
// try {
|
||||||
|
// const abstractSyntaxTree = postcssParse(name + ' {' + value + '}');
|
||||||
|
// const filteredAST = filterCss(abstractSyntaxTree, options.allowedStyles);
|
||||||
|
|
||||||
|
// value = stringifyStyleAttributes(filteredAST);
|
||||||
|
|
||||||
|
// if (value.length === 0) {
|
||||||
|
// delete frame.attribs[a];
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// } catch (e) {
|
||||||
|
// delete frame.attribs[a];
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
result += ' ' + a;
|
||||||
|
if (value && value.length) {
|
||||||
|
result += '="' + escapeHtml(value, true) + '"';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete frame.attribs[a];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (options.selfClosing.indexOf(name) !== -1) {
|
||||||
|
result += ' />';
|
||||||
|
} else {
|
||||||
|
result += '>';
|
||||||
|
if (frame.innerText && !hasText && !options.textFilter) {
|
||||||
|
result += escapeHtml(frame.innerText);
|
||||||
|
addedText = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip) {
|
||||||
|
result = tempResult + escapeHtml(result);
|
||||||
|
tempResult = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ontext: function (text) {
|
||||||
|
if (skipText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastFrame = stack[stack.length - 1];
|
||||||
|
let tag;
|
||||||
|
|
||||||
|
if (lastFrame) {
|
||||||
|
tag = lastFrame.tag;
|
||||||
|
// If inner text was set by transform function then let's use it
|
||||||
|
text = lastFrame.innerText !== undefined ? lastFrame.innerText : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.disallowedTagsMode === 'discard' && ((tag === 'script') || (tag === 'style'))) {
|
||||||
|
// htmlparser2 gives us these as-is. Escaping them ruins the content. Allowing
|
||||||
|
// script tags is, by definition, game over for XSS protection, so if that's
|
||||||
|
// your concern, don't allow them. The same is essentially true for style tags
|
||||||
|
// which have their own collection of XSS vectors.
|
||||||
|
result += text;
|
||||||
|
} else {
|
||||||
|
const escaped = escapeHtml(text, false);
|
||||||
|
if (options.textFilter && !addedText) {
|
||||||
|
result += options.textFilter(escaped, tag);
|
||||||
|
} else if (!addedText) {
|
||||||
|
result += escaped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stack.length) {
|
||||||
|
const frame = stack[stack.length - 1];
|
||||||
|
frame.text += text;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclosetag: function (name) {
|
||||||
|
|
||||||
|
if (skipText) {
|
||||||
|
skipTextDepth--;
|
||||||
|
if (!skipTextDepth) {
|
||||||
|
skipText = false;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const frame = stack.pop();
|
||||||
|
if (!frame) {
|
||||||
|
// Do not crash on bad markup
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skipText = options.enforceHtmlBoundary ? name === 'html' : false;
|
||||||
|
depth--;
|
||||||
|
const skip = skipMap[depth];
|
||||||
|
if (skip) {
|
||||||
|
delete skipMap[depth];
|
||||||
|
if (options.disallowedTagsMode === 'discard') {
|
||||||
|
frame.updateParentNodeText();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tempResult = result;
|
||||||
|
result = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transformMap[depth]) {
|
||||||
|
name = transformMap[depth];
|
||||||
|
delete transformMap[depth];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.exclusiveFilter && options.exclusiveFilter(frame)) {
|
||||||
|
result = result.substr(0, frame.tagPosition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.updateParentNodeMediaChildren();
|
||||||
|
frame.updateParentNodeText();
|
||||||
|
|
||||||
|
if (options.selfClosing.indexOf(name) !== -1) {
|
||||||
|
// Already output />
|
||||||
|
if (skip) {
|
||||||
|
result = tempResult;
|
||||||
|
tempResult = '';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += '</' + name + '>';
|
||||||
|
if (skip) {
|
||||||
|
result = tempResult + escapeHtml(result);
|
||||||
|
tempResult = '';
|
||||||
|
}
|
||||||
|
addedText = false;
|
||||||
|
}
|
||||||
|
}, options.parser);
|
||||||
|
parser.write(html);
|
||||||
|
parser.end();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
function initializeState() {
|
||||||
|
result = '';
|
||||||
|
depth = 0;
|
||||||
|
stack = [];
|
||||||
|
skipMap = {};
|
||||||
|
transformMap = {};
|
||||||
|
skipText = false;
|
||||||
|
skipTextDepth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(s, quote) {
|
||||||
|
if (typeof (s) !== 'string') {
|
||||||
|
s = s + '';
|
||||||
|
}
|
||||||
|
if (options.parser.decodeEntities) {
|
||||||
|
s = s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
if (quote) {
|
||||||
|
s = s.replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: this is inadequate because it will pass `&0;`. This approach
|
||||||
|
// will not work, each & must be considered with regard to whether it
|
||||||
|
// is followed by a 100% syntactically valid entity or not, and escaped
|
||||||
|
// if it is not. If this bothers you, don't set parser.decodeEntities
|
||||||
|
// to false. (The default is true.)
|
||||||
|
s = s.replace(/&(?![a-zA-Z0-9#]{1,20};)/g, '&') // Match ampersands not part of existing HTML entity
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
if (quote) {
|
||||||
|
s = s.replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function naughtyHref(name, href) {
|
||||||
|
// Browsers ignore character codes of 32 (space) and below in a surprising
|
||||||
|
// number of situations. Start reading here:
|
||||||
|
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
href = href.replace(/[\x00-\x20]+/g, '');
|
||||||
|
// Clobber any comments in URLs, which the browser might
|
||||||
|
// interpret inside an XML data island, allowing
|
||||||
|
// a javascript: URL to be snuck through
|
||||||
|
href = href.replace(/<!--.*?-->/g, '');
|
||||||
|
// Case insensitive so we don't get faked out by JAVASCRIPT #1
|
||||||
|
// Allow more characters after the first so we don't get faked
|
||||||
|
// out by certain schemes browsers accept
|
||||||
|
const matches = href.match(/^([a-zA-Z][a-zA-Z0-9.\-+]*):/);
|
||||||
|
if (!matches) {
|
||||||
|
// Protocol-relative URL starting with any combination of '/' and '\'
|
||||||
|
if (href.match(/^[/\\]{2}/)) {
|
||||||
|
return !options.allowProtocolRelative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No scheme
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const scheme = matches[1].toLowerCase();
|
||||||
|
|
||||||
|
if (has(options.allowedSchemesByTag, name)) {
|
||||||
|
return options.allowedSchemesByTag[name].indexOf(scheme) === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !options.allowedSchemes || options.allowedSchemes.indexOf(scheme) === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters user input css properties by allowlisted regex attributes.
|
||||||
|
* Modifies the abstractSyntaxTree object.
|
||||||
|
*
|
||||||
|
* @param {object} abstractSyntaxTree - Object representation of CSS attributes.
|
||||||
|
* @property {array[Declaration]} abstractSyntaxTree.nodes[0] - Each object cointains prop and value key, i.e { prop: 'color', value: 'red' }.
|
||||||
|
* @param {object} allowedStyles - Keys are properties (i.e color), value is list of permitted regex rules (i.e /green/i).
|
||||||
|
* @return {object} - The modified tree.
|
||||||
|
*/
|
||||||
|
// function filterCss(abstractSyntaxTree, allowedStyles) {
|
||||||
|
// if (!allowedStyles) {
|
||||||
|
// return abstractSyntaxTree;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const astRules = abstractSyntaxTree.nodes[0];
|
||||||
|
// let selectedRule;
|
||||||
|
|
||||||
|
// // Merge global and tag-specific styles into new AST.
|
||||||
|
// if (allowedStyles[astRules.selector] && allowedStyles['*']) {
|
||||||
|
// selectedRule = deepmerge(
|
||||||
|
// allowedStyles[astRules.selector],
|
||||||
|
// allowedStyles['*']
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// selectedRule = allowedStyles[astRules.selector] || allowedStyles['*'];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (selectedRule) {
|
||||||
|
// abstractSyntaxTree.nodes[0].nodes = astRules.nodes.reduce(filterDeclarations(selectedRule), []);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return abstractSyntaxTree;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the style attributes from an AbstractSyntaxTree and formats those
|
||||||
|
* values in the inline style attribute format.
|
||||||
|
*
|
||||||
|
* @param {AbstractSyntaxTree} filteredAST
|
||||||
|
* @return {string} - Example: "color:yellow;text-align:center !important;font-family:helvetica;"
|
||||||
|
*/
|
||||||
|
function stringifyStyleAttributes(filteredAST) {
|
||||||
|
return filteredAST.nodes[0].nodes
|
||||||
|
.reduce(function (extractedAttributes, attrObject) {
|
||||||
|
extractedAttributes.push(
|
||||||
|
`${attrObject.prop}:${attrObject.value}${attrObject.important ? ' !important' : ''}`
|
||||||
|
);
|
||||||
|
return extractedAttributes;
|
||||||
|
}, [])
|
||||||
|
.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the existing attributes for the given property. Discards any attributes
|
||||||
|
* which don't match the allowlist.
|
||||||
|
*
|
||||||
|
* @param {object} selectedRule - Example: { color: red, font-family: helvetica }
|
||||||
|
* @param {array} allowedDeclarationsList - List of declarations which pass the allowlist.
|
||||||
|
* @param {object} attributeObject - Object representing the current css property.
|
||||||
|
* @property {string} attributeObject.type - Typically 'declaration'.
|
||||||
|
* @property {string} attributeObject.prop - The CSS property, i.e 'color'.
|
||||||
|
* @property {string} attributeObject.value - The corresponding value to the css property, i.e 'red'.
|
||||||
|
* @return {function} - When used in Array.reduce, will return an array of Declaration objects
|
||||||
|
*/
|
||||||
|
function filterDeclarations(selectedRule) {
|
||||||
|
return function (allowedDeclarationsList, attributeObject) {
|
||||||
|
// If this property is allowlisted...
|
||||||
|
if (has(selectedRule, attributeObject.prop)) {
|
||||||
|
const matchesRegex = selectedRule[attributeObject.prop].some(function (regularExpression) {
|
||||||
|
return regularExpression.test(attributeObject.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchesRegex) {
|
||||||
|
allowedDeclarationsList.push(attributeObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allowedDeclarationsList;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterClasses(classes, allowed, allowedGlobs) {
|
||||||
|
if (!allowed) {
|
||||||
|
// The class attribute is allowed without filtering on this tag
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
classes = classes.split(/\s+/);
|
||||||
|
return classes.filter(function (clss) {
|
||||||
|
return allowed.indexOf(clss) !== -1 || allowedGlobs.some(function (glob) {
|
||||||
|
return glob.test(clss);
|
||||||
|
});
|
||||||
|
}).join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults are accessible to you so that you can use them as a starting point
|
||||||
|
// programmatically if you wish
|
||||||
|
|
||||||
|
const htmlParserDefaults = {
|
||||||
|
decodeEntities: true
|
||||||
|
};
|
||||||
|
sanitizeHtml.defaults = {
|
||||||
|
allowedTags: [
|
||||||
|
// Sections derived from MDN element categories and limited to the more
|
||||||
|
// benign categories.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
||||||
|
// Content sectioning
|
||||||
|
'address', 'article', 'aside', 'footer', 'header',
|
||||||
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hgroup',
|
||||||
|
'main', 'nav', 'section',
|
||||||
|
// Text content
|
||||||
|
'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure',
|
||||||
|
'hr', 'li', 'main', 'ol', 'p', 'pre', 'ul',
|
||||||
|
// Inline text semantics
|
||||||
|
'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn',
|
||||||
|
'em', 'i', 'kbd', 'mark', 'q',
|
||||||
|
'rb', 'rp', 'rt', 'rtc', 'ruby',
|
||||||
|
's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr',
|
||||||
|
// Table content
|
||||||
|
'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th',
|
||||||
|
'thead', 'tr'
|
||||||
|
],
|
||||||
|
disallowedTagsMode: 'discard',
|
||||||
|
allowedAttributes: {
|
||||||
|
a: ['href', 'name', 'target'],
|
||||||
|
// We don't currently allow img itself by default, but
|
||||||
|
// these attributes would make sense if we did.
|
||||||
|
img: ['src', 'srcset', 'alt', 'title', 'width', 'height', 'loading']
|
||||||
|
},
|
||||||
|
// Lots of these won't come up by default because we don't allow them
|
||||||
|
selfClosing: ['img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta'],
|
||||||
|
// URL schemes we permit
|
||||||
|
allowedSchemes: ['http', 'https', 'ftp', 'mailto', 'tel'],
|
||||||
|
allowedSchemesByTag: {},
|
||||||
|
allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],
|
||||||
|
allowProtocolRelative: true,
|
||||||
|
enforceHtmlBoundary: false
|
||||||
|
};
|
||||||
|
|
||||||
|
sanitizeHtml.simpleTransform = function (newTagName, newAttribs, merge) {
|
||||||
|
merge = (merge === undefined) ? true : merge;
|
||||||
|
newAttribs = newAttribs || {};
|
||||||
|
|
||||||
|
return function (tagName, attribs) {
|
||||||
|
let attrib;
|
||||||
|
if (merge) {
|
||||||
|
for (attrib in newAttribs) {
|
||||||
|
attribs[attrib] = newAttribs[attrib];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attribs = newAttribs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tagName: newTagName,
|
||||||
|
attribs: attribs
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
@ -1,4 +1,3 @@
|
|||||||
const { stripHtml } = require('string-strip-html')
|
|
||||||
const { getId } = require('../../utils/index')
|
const { getId } = require('../../utils/index')
|
||||||
const AudioFile = require('../files/AudioFile')
|
const AudioFile = require('../files/AudioFile')
|
||||||
const AudioTrack = require('../files/AudioTrack')
|
const AudioTrack = require('../files/AudioTrack')
|
||||||
@ -78,8 +77,7 @@ class PodcastEpisode {
|
|||||||
episodeType: this.episodeType,
|
episodeType: this.episodeType,
|
||||||
title: this.title,
|
title: this.title,
|
||||||
subtitle: this.subtitle,
|
subtitle: this.subtitle,
|
||||||
// description: this.description,
|
description: this.description,
|
||||||
description: this.descriptionPlain, // Temporary stripping HTML until proper cleaning is implemented
|
|
||||||
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
||||||
pubDate: this.pubDate,
|
pubDate: this.pubDate,
|
||||||
audioFile: this.audioFile.toJSON(),
|
audioFile: this.audioFile.toJSON(),
|
||||||
@ -108,10 +106,6 @@ class PodcastEpisode {
|
|||||||
if (this.episode) return `${this.episode} - ${this.title}`
|
if (this.episode) return `${this.episode} - ${this.title}`
|
||||||
return this.title
|
return this.title
|
||||||
}
|
}
|
||||||
get descriptionPlain() {
|
|
||||||
if (!this.description) return ''
|
|
||||||
return stripHtml(this.description).result
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(data, index = 1) {
|
setData(data, index = 1) {
|
||||||
this.id = getId('ep')
|
this.id = getId('ep')
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const { stripHtml } = require('string-strip-html')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
|
|
||||||
class Audible {
|
class Audible {
|
||||||
@ -17,7 +17,7 @@ class Audible {
|
|||||||
narrator: narrators ? narrators.map(({ name }) => name).join(', ') : null,
|
narrator: narrators ? narrators.map(({ name }) => name).join(', ') : null,
|
||||||
publisher: publisher_name,
|
publisher: publisher_name,
|
||||||
publishedYear: release_date ? release_date.split('-')[0] : null,
|
publishedYear: release_date ? release_date.split('-')[0] : null,
|
||||||
description: publisher_summary ? stripHtml(publisher_summary).result : null,
|
description: publisher_summary ? htmlSanitizer.stripAllTags(publisher_summary) : null,
|
||||||
cover: this.getBestImageLink(product_images),
|
cover: this.getBestImageLink(product_images),
|
||||||
asin,
|
asin,
|
||||||
series: primarySeries ? primarySeries.title : null,
|
series: primarySeries ? primarySeries.title : null,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { stripHtml } = require('string-strip-html')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
|
|
||||||
class iTunes {
|
class iTunes {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class iTunes {
|
|||||||
artistId: data.artistId,
|
artistId: data.artistId,
|
||||||
title: data.collectionName,
|
title: data.collectionName,
|
||||||
author: data.artistName,
|
author: data.artistName,
|
||||||
description: stripHtml(data.description || '').result,
|
description: htmlSanitizer.stripAllTags(data.description || ''),
|
||||||
publishedYear: data.releaseDate ? data.releaseDate.split('-')[0] : null,
|
publishedYear: data.releaseDate ? data.releaseDate.split('-')[0] : null,
|
||||||
genres: data.primaryGenreName ? [data.primaryGenreName] : [],
|
genres: data.primaryGenreName ? [data.primaryGenreName] : [],
|
||||||
cover: this.getCoverArtwork(data)
|
cover: this.getCoverArtwork(data)
|
||||||
@ -83,7 +84,8 @@ class iTunes {
|
|||||||
artistId: data.artistId || null,
|
artistId: data.artistId || null,
|
||||||
title: data.collectionName,
|
title: data.collectionName,
|
||||||
artistName: data.artistName,
|
artistName: data.artistName,
|
||||||
description: stripHtml(data.description || '').result,
|
description: htmlSanitizer.sanitize(data.description || ''),
|
||||||
|
descriptionPlain: htmlSanitizer.stripAllTags(data.description || ''),
|
||||||
releaseDate: data.releaseDate,
|
releaseDate: data.releaseDate,
|
||||||
genres: data.genres || [],
|
genres: data.genres || [],
|
||||||
cover: this.getCoverArtwork(data),
|
cover: this.getCoverArtwork(data),
|
||||||
|
28
server/utils/htmlSanitizer.js
Normal file
28
server/utils/htmlSanitizer.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const sanitizeHtml = require('../libs/sanitizeHtml')
|
||||||
|
|
||||||
|
function sanitize(html) {
|
||||||
|
const sanitizerOptions = {
|
||||||
|
allowedTags: [
|
||||||
|
'p', 'ol', 'ul', 'a', 'strong', 'em'
|
||||||
|
],
|
||||||
|
disallowedTagsMode: 'discard',
|
||||||
|
allowedAttributes: {
|
||||||
|
a: ['href', 'name', 'target']
|
||||||
|
},
|
||||||
|
allowedSchemes: ['https'],
|
||||||
|
allowProtocolRelative: false
|
||||||
|
}
|
||||||
|
|
||||||
|
return sanitizeHtml(html, sanitizerOptions)
|
||||||
|
}
|
||||||
|
module.exports.sanitize = sanitize
|
||||||
|
|
||||||
|
function stripAllTags(html) {
|
||||||
|
const sanitizerOptions = {
|
||||||
|
allowedTags: [],
|
||||||
|
disallowedTagsMode: 'discard'
|
||||||
|
}
|
||||||
|
|
||||||
|
return sanitizeHtml(html, sanitizerOptions)
|
||||||
|
}
|
||||||
|
module.exports.stripAllTags = stripAllTags
|
@ -1,5 +1,5 @@
|
|||||||
const { xmlToJSON } = require('./index')
|
const { xmlToJSON } = require('./index')
|
||||||
const { stripHtml } = require("string-strip-html")
|
const htmlSanitizer = require('./htmlSanitizer')
|
||||||
|
|
||||||
function parseCreators(metadata) {
|
function parseCreators(metadata) {
|
||||||
if (!metadata['dc:creator']) return null
|
if (!metadata['dc:creator']) return null
|
||||||
@ -57,8 +57,7 @@ function fetchDescription(metadata) {
|
|||||||
// check if description is HTML or plain text. only plain text allowed
|
// check if description is HTML or plain text. only plain text allowed
|
||||||
// calibre stores < and > as < and >
|
// calibre stores < and > as < and >
|
||||||
description = description.replace(/</g, '<').replace(/>/g, '>')
|
description = description.replace(/</g, '<').replace(/>/g, '>')
|
||||||
if (description.match(/<!DOCTYPE html>|<\/?\s*[a-z-][^>]*\s*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);)/)) return stripHtml(description).result
|
return htmlSanitizer.stripAllTags(description)
|
||||||
return description
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchGenres(metadata) {
|
function fetchGenres(metadata) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { xmlToJSON } = require('./index')
|
const { xmlToJSON } = require('./index')
|
||||||
const { stripHtml } = require('string-strip-html')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
|
|
||||||
function extractFirstArrayItem(json, key) {
|
function extractFirstArrayItem(json, key) {
|
||||||
if (!json[key] || !json[key].length) return null
|
if (!json[key] || !json[key].length) return null
|
||||||
@ -55,8 +55,9 @@ function extractPodcastMetadata(channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (channel['description']) {
|
if (channel['description']) {
|
||||||
metadata.description = extractFirstArrayItem(channel, 'description')
|
const rawDescription = extractFirstArrayItem(channel, 'description') || ''
|
||||||
metadata.descriptionPlain = stripHtml(metadata.description || '').result
|
metadata.description = htmlSanitizer.sanitize(rawDescription)
|
||||||
|
metadata.descriptionPlain = htmlSanitizer.stripAllTags(rawDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
var arrayFields = ['title', 'language', 'itunes:explicit', 'itunes:author', 'pubDate', 'link']
|
var arrayFields = ['title', 'language', 'itunes:explicit', 'itunes:author', 'pubDate', 'link']
|
||||||
@ -81,8 +82,9 @@ function extractEpisodeData(item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (item['description']) {
|
if (item['description']) {
|
||||||
episode.description = extractFirstArrayItem(item, 'description')
|
const rawDescription = extractFirstArrayItem(item, 'description') || ''
|
||||||
episode.descriptionPlain = stripHtml(episode.description || '').result
|
episode.description = htmlSanitizer.sanitize(rawDescription)
|
||||||
|
episode.descriptionPlain = htmlSanitizer.stripAllTags(rawDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
var arrayFields = ['title', 'pubDate', 'itunes:episodeType', 'itunes:season', 'itunes:episode', 'itunes:author', 'itunes:duration', 'itunes:explicit', 'itunes:subtitle']
|
var arrayFields = ['title', 'pubDate', 'itunes:episodeType', 'itunes:season', 'itunes:episode', 'itunes:author', 'itunes:duration', 'itunes:explicit', 'itunes:subtitle']
|
||||||
|
Loading…
Reference in New Issue
Block a user