fix: sanitize SVG uploads
This commit is contained in:
parent
79e153815f
commit
5d3e81496f
@ -142,6 +142,15 @@
|
||||
:suffix='$t(`admin:security.maxUploadBatchSuffix`)'
|
||||
style='max-width: 450px;'
|
||||
)
|
||||
v-divider.mt-3
|
||||
v-switch(
|
||||
inset
|
||||
label='Scan and Sanitize SVG Uploads'
|
||||
color='primary'
|
||||
v-model='config.uploadScanSVG'
|
||||
persistent-hint
|
||||
hint='Should SVG uploads be scanned for vulnerabilities and stripped of any potentially unsafe content.'
|
||||
)
|
||||
|
||||
v-card.mt-3.animated.fadeInUp.wait-p2s
|
||||
v-toolbar(flat, color='primary', dark, dense)
|
||||
@ -242,6 +251,7 @@ export default {
|
||||
config: {
|
||||
uploadMaxFileSize: 0,
|
||||
uploadMaxFiles: 0,
|
||||
uploadScanSVG: true,
|
||||
securityOpenRedirect: true,
|
||||
securityIframe: true,
|
||||
securityReferrerPolicy: true,
|
||||
@ -286,6 +296,7 @@ export default {
|
||||
$authJwtRenewablePeriod: String
|
||||
$uploadMaxFileSize: Int
|
||||
$uploadMaxFiles: Int
|
||||
$uploadScanSVG: Boolean
|
||||
$securityOpenRedirect: Boolean
|
||||
$securityIframe: Boolean
|
||||
$securityReferrerPolicy: Boolean
|
||||
@ -307,6 +318,7 @@ export default {
|
||||
authJwtRenewablePeriod: $authJwtRenewablePeriod,
|
||||
uploadMaxFileSize: $uploadMaxFileSize,
|
||||
uploadMaxFiles: $uploadMaxFiles,
|
||||
uploadScanSVG: $uploadScanSVG
|
||||
securityOpenRedirect: $securityOpenRedirect,
|
||||
securityIframe: $securityIframe,
|
||||
securityReferrerPolicy: $securityReferrerPolicy,
|
||||
@ -337,6 +349,7 @@ export default {
|
||||
authJwtRenewablePeriod: _.get(this.config, 'authJwtRenewablePeriod', ''),
|
||||
uploadMaxFileSize: _.toSafeInteger(_.get(this.config, 'uploadMaxFileSize', 0)),
|
||||
uploadMaxFiles: _.toSafeInteger(_.get(this.config, 'uploadMaxFiles', 0)),
|
||||
uploadScanSVG: _.get(this.config, 'uploadScanSVG', false),
|
||||
securityOpenRedirect: _.get(this.config, 'securityOpenRedirect', false),
|
||||
securityIframe: _.get(this.config, 'securityIframe', false),
|
||||
securityReferrerPolicy: _.get(this.config, 'securityReferrerPolicy', false),
|
||||
@ -388,6 +401,7 @@ export default {
|
||||
authJwtRenewablePeriod
|
||||
uploadMaxFileSize
|
||||
uploadMaxFiles
|
||||
uploadScanSVG
|
||||
securityOpenRedirect
|
||||
securityIframe
|
||||
securityReferrerPolicy
|
||||
|
@ -80,6 +80,7 @@ defaults:
|
||||
uploads:
|
||||
maxFileSize: 5242880
|
||||
maxFiles: 10
|
||||
scanSVG: true
|
||||
flags:
|
||||
ldapdebug: false
|
||||
sqllog: false
|
||||
|
@ -29,7 +29,8 @@ module.exports = {
|
||||
authJwtExpiration: WIKI.config.auth.tokenExpiration,
|
||||
authJwtRenewablePeriod: WIKI.config.auth.tokenRenewal,
|
||||
uploadMaxFileSize: WIKI.config.uploads.maxFileSize,
|
||||
uploadMaxFiles: WIKI.config.uploads.maxFiles
|
||||
uploadMaxFiles: WIKI.config.uploads.maxFiles,
|
||||
uploadScanSVG: WIKI.config.uploads.scanSVG
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -97,7 +98,8 @@ module.exports = {
|
||||
|
||||
WIKI.config.uploads = {
|
||||
maxFileSize: _.get(args, 'uploadMaxFileSize', WIKI.config.uploads.maxFileSize),
|
||||
maxFiles: _.get(args, 'uploadMaxFiles', WIKI.config.uploads.maxFiles)
|
||||
maxFiles: _.get(args, 'uploadMaxFiles', WIKI.config.uploads.maxFiles),
|
||||
scanSVG: _.get(args, 'uploadScanSVG', WIKI.config.uploads.scanSVG)
|
||||
}
|
||||
|
||||
await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'auth', 'features', 'security', 'uploads'])
|
||||
|
@ -54,6 +54,7 @@ type SiteMutation {
|
||||
securityCSPDirectives: String
|
||||
uploadMaxFileSize: Int
|
||||
uploadMaxFiles: Int
|
||||
uploadScanSVG: Boolean
|
||||
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
}
|
||||
@ -63,15 +64,15 @@ type SiteMutation {
|
||||
# -----------------------------------------------
|
||||
|
||||
type SiteConfig {
|
||||
host: String!
|
||||
title: String!
|
||||
description: String!
|
||||
robots: [String]!
|
||||
analyticsService: String!
|
||||
analyticsId: String!
|
||||
company: String!
|
||||
contentLicense: String!
|
||||
logoUrl: String!
|
||||
host: String
|
||||
title: String
|
||||
description: String
|
||||
robots: [String]
|
||||
analyticsService: String
|
||||
analyticsId: String
|
||||
company: String
|
||||
contentLicense: String
|
||||
logoUrl: String
|
||||
authAutoLogin: Boolean
|
||||
authEnforce2FA: Boolean
|
||||
authHideLocal: Boolean
|
||||
@ -79,18 +80,19 @@ type SiteConfig {
|
||||
authJwtAudience: String
|
||||
authJwtExpiration: String
|
||||
authJwtRenewablePeriod: String
|
||||
featurePageRatings: Boolean!
|
||||
featurePageComments: Boolean!
|
||||
featurePersonalWikis: Boolean!
|
||||
securityOpenRedirect: Boolean!
|
||||
securityIframe: Boolean!
|
||||
securityReferrerPolicy: Boolean!
|
||||
securityTrustProxy: Boolean!
|
||||
securitySRI: Boolean!
|
||||
securityHSTS: Boolean!
|
||||
securityHSTSDuration: Int!
|
||||
securityCSP: Boolean!
|
||||
securityCSPDirectives: String!
|
||||
uploadMaxFileSize: Int!
|
||||
uploadMaxFiles: Int!
|
||||
featurePageRatings: Boolean
|
||||
featurePageComments: Boolean
|
||||
featurePersonalWikis: Boolean
|
||||
securityOpenRedirect: Boolean
|
||||
securityIframe: Boolean
|
||||
securityReferrerPolicy: Boolean
|
||||
securityTrustProxy: Boolean
|
||||
securitySRI: Boolean
|
||||
securityHSTS: Boolean
|
||||
securityHSTSDuration: Int
|
||||
securityCSP: Boolean
|
||||
securityCSPDirectives: String
|
||||
uploadMaxFileSize: Int
|
||||
uploadMaxFiles: Int
|
||||
uploadScanSVG: Boolean
|
||||
}
|
||||
|
25
server/jobs/sanitize-svg.js
Normal file
25
server/jobs/sanitize-svg.js
Normal file
@ -0,0 +1,25 @@
|
||||
const fs = require('fs-extra')
|
||||
const { JSDOM } = require('jsdom')
|
||||
const createDOMPurify = require('dompurify')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = async (svgPath) => {
|
||||
WIKI.logger.info(`Sanitizing SVG file upload...`)
|
||||
|
||||
try {
|
||||
let svgContents = await fs.readFile(svgPath, 'utf8')
|
||||
|
||||
const window = new JSDOM('').window
|
||||
const DOMPurify = createDOMPurify(window)
|
||||
|
||||
svgContents = DOMPurify.sanitize(svgContents)
|
||||
|
||||
await fs.writeFile(svgPath, svgContents)
|
||||
WIKI.logger.info(`Sanitized SVG file upload: [ COMPLETED ]`)
|
||||
} catch (err) {
|
||||
WIKI.logger.error(`Failed to sanitize SVG file upload: [ FAILED ]`)
|
||||
WIKI.logger.error(err.message)
|
||||
throw err
|
||||
}
|
||||
}
|
@ -99,6 +99,16 @@ module.exports = class Asset extends Model {
|
||||
folderId: opts.folderId
|
||||
}
|
||||
|
||||
// Sanitize SVG contents
|
||||
if (WIKI.config.uploads.scanSVG && opts.mimetype === 'image/svg+xml') {
|
||||
const svgSanitizeJob = await WIKI.scheduler.registerJob({
|
||||
name: 'sanitize-svg',
|
||||
immediate: true,
|
||||
worker: true
|
||||
}, opts.path)
|
||||
await svgSanitizeJob.finished
|
||||
}
|
||||
|
||||
// Save asset data
|
||||
try {
|
||||
const fileBuffer = await fs.readFile(opts.path)
|
||||
|
Loading…
Reference in New Issue
Block a user