diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index dab80590..a4360889 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,5 +11,5 @@ jobs:
- uses: yandex-cloud/ui-release-action@main
with:
github-token: ${{ secrets.YC_UI_BOT_GITHUB_TOKEN }}
- npm-token: ${{ secrets.YC_UI_BOT_NPM_TOKEN }}
+ npm-token: ${{ secrets.ROBOT_DATAUI_NPM_TOKEN }}
node-version: 14
diff --git a/CHANGELOG.diplodoc.md b/CHANGELOG.diplodoc.md
new file mode 100644
index 00000000..23c0d805
--- /dev/null
+++ b/CHANGELOG.diplodoc.md
@@ -0,0 +1,38 @@
+# Changelog @diplodoc/transform@4.0.0
+
+## It's major update of @doc-tools/transform@3.11.0 with security changes.
+
+### New term's linter
+
+The main feature of term is generating a hidden content, that will be show on click. Terms plugins creates MarkadownIt tokens at place, where term was defined and it can brake our `@doc-tools/docs` navigation. Now `@diplodoc/transform` has new yfmlint rule: `no-term-definition-in-content`. There are several restrictions: - You can't define content between term-def - All term-def should be placed at the end of file.
+
+### Enabling `needToSanitizeHtml` by default
+
+The sanitizer includes default options with safe, allowed tags, and attributes. However, by default, both the `style` tag and the `style` attribute are also allowed. The values will be processed by the [cssfilter](https://github.com/leizongmin/js-css-filter) module to prevent XSS attacks. The cssfilter module includes a default CSS whitelist.
+
+You can override the options for sanitizer like this:
+
+```javascript
+const transform = require('@doc-tools/transform');
+const {defaultOptions} = require('@doc-tools/transform/lib/sanitize');
+
+const sanitizeOptions = Object.assign({}, defaultOptions);
+
+// Allow css property
+sanitizeOptions.cssWhiteList['position'] = true;
+
+// Disallow css property
+delete sanitizeOptions.cssWhiteList['color'];
+
+// Disable `style` tag
+sanitizeOptions.allowedTags = sanitizeOptions.allowedTags.filter((tag) => tag !== 'style');
+
+// Disable `style` attribute
+sanitizeOptions.allowedAttributes['*'] = sanitizeOptions.allowedAttributes['*'].filter(
+ (attr) => attr !== 'style',
+);
+
+const {
+ result: {html},
+} = transform(content, {sanitizeOptions});
+```
diff --git a/package-lock.json b/package-lock.json
index 39c00cbc..d4e2fde1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
- "name": "@doc-tools/transform",
- "version": "3.11.0",
+ "name": "@diplodoc/transform",
+ "version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -4248,6 +4248,12 @@
"@babel/types": "^7.3.0"
}
},
+ "@types/css": {
+ "version": "0.0.34",
+ "resolved": "https://registry.npmjs.org/@types/css/-/css-0.0.34.tgz",
+ "integrity": "sha512-nyZiWXt6gBG7Jj7BYB4zdHXyrhcAjJH8qv+BUsO8FMJR5be7H5ETOaibB3uvXeX5lc57LWkecNJv03q0+JpbXA==",
+ "dev": true
+ },
"@types/github-slugger": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz",
@@ -4687,6 +4693,11 @@
"integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=",
"dev": true
},
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
+ },
"autoprefixer": {
"version": "10.4.15",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz",
@@ -4894,6 +4905,11 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -5002,6 +5018,114 @@
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
"dev": true
},
+ "cheerio": {
+ "version": "1.0.0-rc.12",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
+ "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
+ "requires": {
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "htmlparser2": "^8.0.1",
+ "parse5": "^7.0.0",
+ "parse5-htmlparser2-tree-adapter": "^7.0.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ }
+ },
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+ },
+ "htmlparser2": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "entities": "^4.4.0"
+ }
+ }
+ }
+ },
+ "cheerio-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-select": "^5.1.0",
+ "css-what": "^6.1.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ }
+ },
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+ }
+ }
+ },
"chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
@@ -5044,7 +5168,7 @@
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
"dev": true
},
"collect-v8-coverage": {
@@ -5110,6 +5234,16 @@
"which": "^2.0.1"
}
},
+ "css": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
+ "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
+ "requires": {
+ "inherits": "^2.0.4",
+ "source-map": "^0.6.1",
+ "source-map-resolve": "^0.6.0"
+ }
+ },
"css-blank-pseudo": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-6.0.0.tgz",
@@ -5136,6 +5270,58 @@
"integrity": "sha512-03QGAk/FXIRseDdLb7XAiu6gidQ0Nd8945xuM7VFVPpc6goJsG9uIO8xQjTxwbPdPIIV4o4AJoOJyt8gwDl67g==",
"dev": true
},
+ "css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ }
+ },
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+ }
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
+ },
"cssdb": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz",
@@ -5148,6 +5334,11 @@
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true
},
+ "cssfilter": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+ "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="
+ },
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
@@ -5157,10 +5348,15 @@
"ms": "2.1.2"
}
},
+ "decode-uri-component": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
+ },
"dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
- "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
+ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
"dev": true
},
"deep-eql": {
@@ -5765,7 +5961,7 @@
"exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
"dev": true
},
"expect": {
@@ -5815,7 +6011,7 @@
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
"fastq": {
@@ -6148,7 +6344,7 @@
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true
},
"inflight": {
@@ -6164,8 +6360,7 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"internal-slot": {
"version": "1.0.3",
@@ -6930,7 +7125,7 @@
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
"json5": {
@@ -7002,13 +7197,13 @@
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"dev": true
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
"dev": true
},
"lodash.merge": {
@@ -7188,7 +7383,7 @@
"mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
- "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
+ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
},
"merge-stream": {
"version": "2.0.0",
@@ -7241,13 +7436,13 @@
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
"node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
"dev": true
},
"node-releases": {
@@ -7277,6 +7472,14 @@
"path-key": "^3.0.0"
}
},
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -7432,6 +7635,40 @@
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
},
+ "parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "requires": {
+ "entities": "^4.4.0"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+ }
+ }
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
+ "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
+ "requires": {
+ "domhandler": "^5.0.2",
+ "parse5": "^7.0.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
+ }
+ },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -8174,7 +8411,7 @@
"safe-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
"dev": true,
"requires": {
"ret": "~0.1.10"
@@ -8269,14 +8506,22 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
},
+ "source-map-resolve": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
+ "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0"
+ }
+ },
"source-map-support": {
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
@@ -8443,7 +8688,7 @@
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"tmpl": {
@@ -8603,7 +8848,7 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
"v8-compile-cache": {
diff --git a/package.json b/package.json
index a5c59049..6774a32b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
- "name": "@doc-tools/transform",
- "version": "3.11.0",
+ "name": "@diplodoc/transform",
+ "version": "4.0.0",
"description": "A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML",
"author": "YFM Team {% file src="../file" %} {% file name="file.txt" %} {% file src="../file" %} {% file name="file.txt" %}const x: string = 'y';
+"
"
`;
diff --git a/test/checkbox.test.ts b/test/checkbox.test.ts
index 618efff5..fc4c352a 100644
--- a/test/checkbox.test.ts
+++ b/test/checkbox.test.ts
@@ -82,7 +82,7 @@ describe('markdown-it-checkbox', function () {
it('should parse inline markup in label', () => {
expect(transformYfm('[X] text *italic* **bold** label')).toBe(
'const x: string = 'y';
{% file src="index.txt"name="index.html"type="text/html" %}
\n`, + `{% file src="index.txt"name="index.html"type="text/html" %}
\n`, ); }); diff --git a/test/markdownlint-custom-rules/yfm004.test.ts b/test/markdownlint-custom-rules/yfm004.test.ts index 0ccf6dc4..9484b660 100644 --- a/test/markdownlint-custom-rules/yfm004.test.ts +++ b/test/markdownlint-custom-rules/yfm004.test.ts @@ -22,10 +22,12 @@ const tableWithCloseToken = ` |# `.trim(); +const random = () => Math.floor(Math.random() * 1e8); + const lint = (input: string) => { yfmlint({ input, - pluginOptions: {log, path: 'test.md'}, + pluginOptions: {log, path: `${random()}.md`}, lintConfig: { 'log-levels': { MD047: LogLevels.DISABLED, diff --git a/test/markdownlint-custom-rules/yfm009.test.ts b/test/markdownlint-custom-rules/yfm009.test.ts new file mode 100644 index 00000000..79cbcf0c --- /dev/null +++ b/test/markdownlint-custom-rules/yfm009.test.ts @@ -0,0 +1,68 @@ +import {log, LogLevels} from '../../src/transform/log'; +import term from '../../src/transform/plugins/term'; +import yfmlint from '../../src/transform/yfmlint'; +import {yfm009} from '../../src/transform/yfmlint/markdownlint-custom-rule'; + +const withIncludes = `\ +[*widget-popup1]: {% include [ ](./_includes/widget.md) %} + +[*button-popup2]: {% include [ ](./_includes/button) %} + +[*button-popup3]: {% include [ ](./_includes/button) %}`.trim(); + +const plainText = `\ +[*widget-popup1]: hello world + +hi i am text + +[*widget-popup2]: it's will fail`.trim(); + +const appendText = (text: string) => { + return `${text}\n\nhi i am text after`; +}; + +const prependText = (text: string) => { + return `hi i am text before\n\n${text}`; +}; + +const random = () => Math.floor(Math.random() * 1e8); + +const lint = (input: string) => { + yfmlint({ + input, + pluginOptions: {log, path: `${random()}.md`}, + lintConfig: { + 'log-levels': { + YFM009: LogLevels.ERROR, + }, + }, + customLintRules: [yfm009], + plugins: [term], + }); +}; + +describe('YFM009', () => { + beforeEach(() => { + log.clear(); + }); + + it('not accepts text between terms', () => { + lint(plainText); + expect(log.get().error[0]).toMatch('YFM009'); + }); + + it('not accepts includes with text after', () => { + lint(appendText(withIncludes)); + expect(log.get().error[0]).toMatch('YFM009'); + }); + + it('accepts includes', () => { + lint(withIncludes); + expect(log.isEmpty()).toBeTruthy(); + }); + + it('accepts includes with text before', () => { + lint(prependText(withIncludes)); + expect(log.isEmpty()).toBeTruthy(); + }); +}); diff --git a/test/sanitize-html.test.ts b/test/sanitize-html.test.ts index 997e392c..69ff1095 100644 --- a/test/sanitize-html.test.ts +++ b/test/sanitize-html.test.ts @@ -1,5 +1,5 @@ import transform from '../src/transform'; -import sanitizeHtml from '../src/transform/sanitize'; +import sanitizeHtml, {defaultOptions} from '../src/transform/sanitize'; const transformYfm = (text: string, options?: ParametersClick
\n', + ); + }); + + it('should not sanitize safe attributes', () => { + expect(transformYfm('Click {.style-me data-toggle=modal}')).toBe( + 'Click
\n', + ); + }); + + it('should sanitize danger style attributes', () => { + expect( + transformYfm( + '[example.com](https://example.com){style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5"}', + ), + ).toBe( + '\n', + ); + }); + }); }); - it('by default transform should not sanitize html', () => { - expect(transformYfm('')).toBe(''); + describe('rewrite default sanitize options', () => { + it('should not sanitize form tag if form is allowed', () => { + const sanitizeOptions = Object.assign({}, defaultOptions); + + // @ts-ignore + sanitizeOptions.allowedTags = sanitizeOptions.allowedTags.concat(['form']); + + expect( + transformYfm('', { + sanitizeOptions, + }), + ).toBe(''); + }); + + it('should filter style tag', () => { + const sanitizeOptions = Object.assign({}, defaultOptions); + + // @ts-ignore + sanitizeOptions.allowedTags = sanitizeOptions.allowedTags.filter( + (tag: string) => tag !== 'style', + ); + + expect( + transformYfm('', { + sanitizeOptions, + }), + ).toBe(''); + }); + + it('should filter style attribute', () => { + const sanitizeOptions = Object.assign({}, defaultOptions); + + // @ts-ignore + sanitizeOptions.allowedAttributes['*'] = sanitizeOptions.allowedAttributes['*'].filter( + (attr: string) => attr !== 'style', + ); + + expect( + transformYfm('', { + sanitizeOptions, + }), + ).toBe(''); + }); + + it('should not sanitize property if it is passed in cssWhiteList', () => { + const sanitizeOptions = Object.assign({}, defaultOptions); + + // @ts-ignore + sanitizeOptions.cssWhiteList['position'] = true; + + expect( + transformYfm('', { + sanitizeOptions, + }), + ).toBe(''); + }); + }); + + it('transform should not sanitize html if needToSanitizeHtml is false', () => { + expect(transformYfm('', {needToSanitizeHtml: false})).toBe( + '', + ); }); }); diff --git a/test/table.test.ts b/test/table.test.ts index 91ddf78f..75bd74b9 100644 --- a/test/table.test.ts +++ b/test/table.test.ts @@ -150,7 +150,7 @@ describe('Table plugin', () => { '\n' + 'Text with
\n' +
+ '
Text with
\n' +
'new line
#|
\n' +
- '||Cell in column 1, row 1
\n' +
- '|Cell in column 2, row 1||
\n' +
- '||Cell in column 1, row 2
\n' +
- '|Cell in column 2, row 2||
\n' +
- '||Cell in column 1, row 3
\n' +
+ '
#|
\n' +
+ '||Cell in column 1, row 1
\n' +
+ '|Cell in column 2, row 1||
\n' +
+ '||Cell in column 1, row 2
\n' +
+ '|Cell in column 2, row 2||
\n' +
+ '||Cell in column 1, row 3
\n' +
'|Cell in column 2, row 3||
||Cell in column 1, row 1
\n' +
- '|Cell in column 2, row 1
\n' +
- '||Cell in column 1, row 2
\n' +
- '|Cell in column 2, row 2
\n' +
+ '
||Cell in column 1, row 1
\n' +
+ '|Cell in column 2, row 1
\n' +
+ '||Cell in column 1, row 2
\n' +
+ '|Cell in column 2, row 2
\n' +
'|#
Test
\n', ); @@ -392,12 +391,12 @@ describe('Table plugin', () => { '|#', ), ).toBe( - 'Test
\n' +
- '#|
\n' +
- '||Cell in column 1, row 1
\n' +
+ '
Test
\n' +
+ '#|
\n' +
+ '||Cell in column 1, row 1
\n' +
'|Cell in column 2, row 1||
||Cell in column 1, row 2
\n' +
- '|Cell in column 2, row 2||
\n' +
+ '
||Cell in column 1, row 2
\n' +
+ '|Cell in column 2, row 2||
\n' +
'|#
';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//-->">'>
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('XSS locator 2', () => { + const input = `'';!--"'';!--"=&
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('No Filter Evasion', () => { + const input = ``; + const expected = ''; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Image XSS using the JavaScript directive', () => { + const input = ``; + const expected = ''; + + expect(transformYfm(input)).toBe(expected); + }); + + it('No quotes and no semicolon', () => { + const input = ``; + const expected = `<img src=javascript:alert('XSS')>
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Case insensitive XSS attack vector', () => { + const input = ``; + const expected = `<img src=JaVaScRiPt:alert('XSS')>
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('HTML entities', () => { + const input = ``; + const expected = `<img src=javascript:alert("XSS")>
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Grave accent obfuscation', () => { + const input = ""; + const expected = `<img src='javascript:alert("RSnake says, 'XSS'")'>
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Malformed A tags', () => { + const input = 'xxs link'; + const expected = `\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Malformed img tags', () => { + const input = '">'; + const expected = `<img """>">
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('fromCharCode', () => { + const input = ''; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Default src tag to get past filters that check src domain', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Default src tag by leaving it empty', () => { + const input = ``; + const expected = `<img src= onmouseover="alert('xxs')">
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Default src tag by leaving it out entirely', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Decimal HTML character references', () => { + const input = ``; + const expected = `<img src=javascript:alert(
\n'XSS')>
<img src=javascript:a&
\n#0000108ert('XSS')>
perl -e 'print "<img src=java�script:alert("XSS")>";' > out
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Spaces and meta chars before the JavaScript in images for XSS', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Non-alpha-non-digit XSS 1', () => { + const input = ``; + const expected = `<script/xss src="http://ha.ckers.org/xss.js">
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Non-alpha-non-digit XSS 2', () => { + const input = ''; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Non-alpha-non-digit XSS 3', () => { + const input = ''; + const expected = `<script/src="http://ha.ckers.org/xss.js">
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('Non-alpha-non-digit XSS 4', () => { + const input = '<'; + const expected = `<
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('No closing script tags', () => { + const input = '`; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('input image', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('body image', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('img Dynsrc', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('img lowsrc', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('List-style-image', () => { + const input = `¼script¾alert(¢XSS¢)¼/script¾
\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('meta', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('iframe', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('iframe Event based', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('frame', () => { + const input = ``; + const expected = ``; + + expect(transformYfm(input)).toBe(expected); + }); + + it('TABLE', () => { + const input = ``;
+ const expected = ` `;
+ const expected = ``;
+
+ expect(transformYfm(input)).toBe(expected);
+ });
+
+ it('DIV background-image with unicoded XSS exploit', () => {
+ const input = ` `;
+ const expected = ``;
+
+ expect(transformYfm(input)).toBe(expected);
+ });
+
+ it('DIV background-image with unicoded XSS exploit 2', () => {
+ const input = ` `;
+ const expected = ``;
+
+ expect(transformYfm(input)).toBe(expected);
+ });
+
+ it('DIV expression', () => {
+ const input = ` `;
+ const expected = ``;
+
+ expect(transformYfm(input)).toBe(expected);
+ });
+
+ it('Downlevel-Hidden block', () => {
+ const input = ``;
+ const expected = ``;
+
+ expect(transformYfm(input)).toBe(expected);
+ });
+
+ it('BASE tag', () => {
+ const input = ` <embed src="http://ha.ckers.Using an EMBED tag you can embed a Flash movie that contains XSS. Click here for a demo. If you add the attributes allowScriptAccess="never" and allownetworking="internal" it can mitigate this risk (thank you to Jonathan Vanasco for the info).:org/xss.swf" AllowScriptAccess="always"> \n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('You can EMBED SVG which can contain your XSS vector', () => { + const input = ``; + const expected = `\n`; + + expect(transformYfm(input)).toBe(expected); + }); + + it('XML tag', () => { + const input = ` |