fix: force download of unsafe extensions
This commit is contained in:
parent
57b56d3a5b
commit
79bdd44093
@ -151,6 +151,15 @@
|
||||
persistent-hint
|
||||
hint='Should SVG uploads be scanned for vulnerabilities and stripped of any potentially unsafe content.'
|
||||
)
|
||||
v-divider.mt-3
|
||||
v-switch(
|
||||
inset
|
||||
label='Force Download of Unsafe Extensions'
|
||||
color='primary'
|
||||
v-model='config.uploadForceDownload'
|
||||
persistent-hint
|
||||
hint='Should non-image files be forced as downloads when accessed directly. This prevents potential XSS attacks via unsafe file extensions uploads.'
|
||||
)
|
||||
|
||||
v-card.mt-3.animated.fadeInUp.wait-p2s
|
||||
v-toolbar(flat, color='primary', dark, dense)
|
||||
@ -252,6 +261,7 @@ export default {
|
||||
uploadMaxFileSize: 0,
|
||||
uploadMaxFiles: 0,
|
||||
uploadScanSVG: true,
|
||||
uploadForceDownload: true,
|
||||
securityOpenRedirect: true,
|
||||
securityIframe: true,
|
||||
securityReferrerPolicy: true,
|
||||
@ -297,6 +307,7 @@ export default {
|
||||
$uploadMaxFileSize: Int
|
||||
$uploadMaxFiles: Int
|
||||
$uploadScanSVG: Boolean
|
||||
$uploadForceDownload: Boolean
|
||||
$securityOpenRedirect: Boolean
|
||||
$securityIframe: Boolean
|
||||
$securityReferrerPolicy: Boolean
|
||||
@ -319,6 +330,7 @@ export default {
|
||||
uploadMaxFileSize: $uploadMaxFileSize,
|
||||
uploadMaxFiles: $uploadMaxFiles,
|
||||
uploadScanSVG: $uploadScanSVG
|
||||
uploadForceDownload: $uploadForceDownload,
|
||||
securityOpenRedirect: $securityOpenRedirect,
|
||||
securityIframe: $securityIframe,
|
||||
securityReferrerPolicy: $securityReferrerPolicy,
|
||||
@ -350,6 +362,7 @@ export default {
|
||||
uploadMaxFileSize: _.toSafeInteger(_.get(this.config, 'uploadMaxFileSize', 0)),
|
||||
uploadMaxFiles: _.toSafeInteger(_.get(this.config, 'uploadMaxFiles', 0)),
|
||||
uploadScanSVG: _.get(this.config, 'uploadScanSVG', false),
|
||||
uploadForceDownload: _.get(this.config, 'uploadForceDownload', false),
|
||||
securityOpenRedirect: _.get(this.config, 'securityOpenRedirect', false),
|
||||
securityIframe: _.get(this.config, 'securityIframe', false),
|
||||
securityReferrerPolicy: _.get(this.config, 'securityReferrerPolicy', false),
|
||||
@ -402,6 +415,7 @@ export default {
|
||||
uploadMaxFileSize
|
||||
uploadMaxFiles
|
||||
uploadScanSVG
|
||||
uploadForceDownload
|
||||
securityOpenRedirect
|
||||
securityIframe
|
||||
securityReferrerPolicy
|
||||
|
@ -81,6 +81,7 @@ defaults:
|
||||
maxFileSize: 5242880
|
||||
maxFiles: 10
|
||||
scanSVG: true
|
||||
forceDownload: true
|
||||
flags:
|
||||
ldapdebug: false
|
||||
sqllog: false
|
||||
|
@ -30,7 +30,8 @@ module.exports = {
|
||||
authJwtRenewablePeriod: WIKI.config.auth.tokenRenewal,
|
||||
uploadMaxFileSize: WIKI.config.uploads.maxFileSize,
|
||||
uploadMaxFiles: WIKI.config.uploads.maxFiles,
|
||||
uploadScanSVG: WIKI.config.uploads.scanSVG
|
||||
uploadScanSVG: WIKI.config.uploads.scanSVG,
|
||||
uploadForceDownload: WIKI.config.uploads.forceDownload
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -99,7 +100,8 @@ module.exports = {
|
||||
WIKI.config.uploads = {
|
||||
maxFileSize: _.get(args, 'uploadMaxFileSize', WIKI.config.uploads.maxFileSize),
|
||||
maxFiles: _.get(args, 'uploadMaxFiles', WIKI.config.uploads.maxFiles),
|
||||
scanSVG: _.get(args, 'uploadScanSVG', WIKI.config.uploads.scanSVG)
|
||||
scanSVG: _.get(args, 'uploadScanSVG', WIKI.config.uploads.scanSVG),
|
||||
forceDownload: _.get(args, 'uploadForceDownload', WIKI.config.uploads.forceDownload)
|
||||
}
|
||||
|
||||
await WIKI.configSvc.saveToDb(['host', 'title', 'company', 'contentLicense', 'seo', 'logoUrl', 'auth', 'features', 'security', 'uploads'])
|
||||
|
@ -55,6 +55,7 @@ type SiteMutation {
|
||||
uploadMaxFileSize: Int
|
||||
uploadMaxFiles: Int
|
||||
uploadScanSVG: Boolean
|
||||
uploadForceDownload: Boolean
|
||||
|
||||
): DefaultResponse @auth(requires: ["manage:system"])
|
||||
}
|
||||
@ -95,4 +96,5 @@ type SiteConfig {
|
||||
uploadMaxFileSize: Int
|
||||
uploadMaxFiles: Int
|
||||
uploadScanSVG: Boolean
|
||||
uploadForceDownload: Boolean
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
const crypto = require('crypto')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@ -6,5 +7,9 @@ module.exports = {
|
||||
*/
|
||||
generateHash(assetPath) {
|
||||
return crypto.createHash('sha1').update(assetPath).digest('hex')
|
||||
},
|
||||
|
||||
getPathInfo(assetPath) {
|
||||
return path.parse(assetPath.toLowerCase())
|
||||
}
|
||||
}
|
||||
|
@ -168,8 +168,15 @@ module.exports = class Asset extends Model {
|
||||
|
||||
static async getAsset(assetPath, res) {
|
||||
try {
|
||||
const fileInfo = assetHelper.getPathInfo(assetPath)
|
||||
const fileHash = assetHelper.generateHash(assetPath)
|
||||
const cachePath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${fileHash}.dat`)
|
||||
|
||||
// Force unsafe extensions to download
|
||||
if (WIKI.config.uploads.forceDownload && !['.png', '.apng', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.svg'].includes(fileInfo.ext)) {
|
||||
res.set('Content-disposition', 'attachment; filename=' + fileInfo.base)
|
||||
}
|
||||
|
||||
if (await WIKI.models.assets.getAssetFromCache(assetPath, cachePath, res)) {
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user