Skip to content

Commit 810e8db

Browse files
committed
fix: sanitize category svg image files
1 parent 1e6c6f4 commit 810e8db

File tree

1 file changed

+40
-0
lines changed

1 file changed

+40
-0
lines changed

src/controllers/admin/uploads.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const path = require('path');
44
const nconf = require('nconf');
55
const fs = require('fs');
6+
const sanitizeHtml = require('sanitize-html');
67

78
const meta = require('../../meta');
89
const posts = require('../../posts');
@@ -121,11 +122,50 @@ uploadsController.uploadCategoryPicture = async function (req, res, next) {
121122
return next(new Error('[[error:invalid-json]]'));
122123
}
123124

125+
if (uploadedFile.path.endsWith('.svg')) {
126+
await sanitizeSvg(uploadedFile.path);
127+
}
128+
124129
await validateUpload(uploadedFile, allowedImageTypes);
125130
const filename = `category-${params.cid}${path.extname(uploadedFile.name)}`;
126131
await uploadImage(filename, 'category', uploadedFile, req, res, next);
127132
};
128133

134+
async function sanitizeSvg(filePath) {
135+
const dirty = await fs.promises.readFile(filePath, 'utf8');
136+
const clean = sanitizeHtml(dirty, {
137+
allowedTags: [
138+
'svg', 'g', 'defs', 'linearGradient', 'radialGradient', 'stop',
139+
'circle', 'ellipse', 'polygon', 'polyline', 'path', 'rect',
140+
'line', 'text', 'tspan', 'use', 'symbol', 'clipPath', 'mask', 'pattern',
141+
'filter', 'feGaussianBlur', 'feOffset', 'feBlend', 'feColorMatrix', 'feMerge', 'feMergeNode',
142+
],
143+
allowedAttributes: {
144+
'*': [
145+
// Geometry
146+
'x', 'y', 'x1', 'x2', 'y1', 'y2', 'cx', 'cy', 'r', 'rx', 'ry',
147+
'width', 'height', 'd', 'points', 'viewBox', 'transform',
148+
149+
// Presentation
150+
'fill', 'stroke', 'stroke-width', 'opacity',
151+
'stop-color', 'stop-opacity', 'offset', 'style', 'class',
152+
153+
// Text
154+
'text-anchor', 'font-size', 'font-family',
155+
156+
// Misc
157+
'id', 'clip-path', 'mask', 'filter', 'gradientUnits', 'gradientTransform',
158+
'xmlns', 'preserveAspectRatio',
159+
],
160+
},
161+
parser: {
162+
lowerCaseTags: false,
163+
lowerCaseAttributeNames: false,
164+
},
165+
});
166+
await fs.promises.writeFile(filePath, clean);
167+
}
168+
129169
uploadsController.uploadFavicon = async function (req, res, next) {
130170
const uploadedFile = req.files.files[0];
131171
const allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'];

0 commit comments

Comments
 (0)