Skip to content

Commit

Permalink
chore: Add the eslint-no-unsanitized rule (#85)
Browse files Browse the repository at this point in the history
This commit adds the eslint-plugin-no-unsanitized rule, which flags most usages of .innerHTML and document.write as unsafe.
  • Loading branch information
personalizedrefrigerator authored Nov 12, 2024
1 parent 73de8fe commit 0970ab8
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 146 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:no-unsanitized/recommended-legacy',
'eslint-config-prettier',
],
// See https://typescript-eslint.io/linting/troubleshooting/#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file
Expand Down
15 changes: 10 additions & 5 deletions docs/demo/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ export const makeIconFromText = (text: string) => {

export const makeLocalStorageIcon = () => {
const elem = document.createElementNS(svgNamespace, 'svg');
const iconFillSStyle = 'style="fill: var(--icon-color);"';
elem.innerHTML = `
<path d="M 50,10 V 60 H 35 L 55,85 75,60 H 60 V 10 Z" ${iconFillSStyle}/>
<path d="m 15,85 v 10 h 85 V 85 Z" ${iconFillSStyle}/>
`;

const makePath = (d: string) => {
const path = document.createElementNS(svgNamespace, 'path');
path.style.fill = 'var(--icon-color)';
path.setAttribute('d', d);
elem.appendChild(path);
};

makePath('M 50,10 V 60 H 35 L 55,85 75,60 H 60 V 10 Z');
makePath('m 15,85 v 10 h 85 V 85 Z');
elem.setAttribute('viewBox', '5 0 100 100');

return elem;
Expand Down
7 changes: 4 additions & 3 deletions docs/demo/ui/makeNewImageDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@ const makeNewImageDialog = (
const templateButton = document.createElement('button');
templateButton.classList.add('new-image-template-button');

const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.innerHTML = svgData;
const icon = new Image();
icon.classList.add('icon');
icon.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgData)}`;
const label = document.createElement('div');
label.innerText = title;
label.textContent = title;

templateButton.onclick = () => {
closeDialogWithStringResult(svgData);
Expand Down
6 changes: 5 additions & 1 deletion docs/demo/ui/newImageDialog.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
}

.new-image-template-button .icon {
width: 95%;
width: 300px;
height: 150px;

object-position: left top;
object-fit: none; /* Do not resize to fit container */
}

.new-image-template-button:hover {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@typescript-eslint/parser": "8.4.0",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-no-unsanitized": "4.1.2",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"jsdom": "25.0.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/js-draw/src/SVGLoader/SVGLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,7 @@ export default class SVGLoader implements ImageLoader {
const { svgElem, cleanUp } = (() => {
// If the user requested an iframe load (the default) try to load with an iframe.
// There are some cases (e.g. in a sandboxed iframe) where this doesn't work.
// TODO(v2): Use domParserLoad by default.
if (!domParserLoad) {
try {
const sandbox = document.createElement('iframe');
Expand Down Expand Up @@ -744,6 +745,7 @@ export default class SVGLoader implements ImageLoader {
sandboxDoc.close();

const svgElem = sandboxDoc.createElementNS('http://www.w3.org/2000/svg', 'svg');
// eslint-disable-next-line no-unsanitized/property -- setting innerHTML in a sandboxed document.
svgElem.innerHTML = text;
sandboxDoc.body.appendChild(svgElem);

Expand Down
8 changes: 6 additions & 2 deletions packages/js-draw/src/rendering/renderers/SVGRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ export default class SVGRenderer extends AbstractRenderer {
if (!this.elem.querySelector(`#${renderedStylesheetId}`)) {
// Default to rounded strokes.
const styleSheet = document.createElementNS('http://www.w3.org/2000/svg', 'style');
styleSheet.innerHTML = `
styleSheet.appendChild(
document.createTextNode(
`
path {
stroke-linecap: round;
stroke-linejoin: round;
Expand All @@ -80,7 +82,9 @@ export default class SVGRenderer extends AbstractRenderer {
text {
white-space: pre;
}
`.replace(/\s+/g, '');
`.replace(/\s+/g, ''),
),
);
styleSheet.setAttribute('id', renderedStylesheetId);
this.elem.appendChild(styleSheet);
}
Expand Down
Loading

0 comments on commit 0970ab8

Please sign in to comment.