Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wiki-js
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
wiki-js
Commits
5d3e8149
Unverified
Commit
5d3e8149
authored
Dec 17, 2021
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: sanitize SVG uploads
parent
79e15381
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
79 additions
and
25 deletions
+79
-25
admin-security.vue
client/components/admin/admin-security.vue
+14
-0
data.yml
server/app/data.yml
+1
-0
site.js
server/graph/resolvers/site.js
+4
-2
site.graphql
server/graph/schemas/site.graphql
+25
-23
sanitize-svg.js
server/jobs/sanitize-svg.js
+25
-0
assets.js
server/models/assets.js
+10
-0
No files found.
client/components/admin/admin-security.vue
View file @
5d3e8149
...
@@ -142,6 +142,15 @@
...
@@ -142,6 +142,15 @@
:suffix='$t(`admin:security.maxUploadBatchSuffix`)'
:suffix='$t(`admin:security.maxUploadBatchSuffix`)'
style='max-width: 450px;'
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-card.mt-3.animated.fadeInUp.wait-p2s
v-toolbar(flat, color='primary', dark, dense)
v-toolbar(flat, color='primary', dark, dense)
...
@@ -242,6 +251,7 @@ export default {
...
@@ -242,6 +251,7 @@ export default {
config
:
{
config
:
{
uploadMaxFileSize
:
0
,
uploadMaxFileSize
:
0
,
uploadMaxFiles
:
0
,
uploadMaxFiles
:
0
,
uploadScanSVG
:
true
,
securityOpenRedirect
:
true
,
securityOpenRedirect
:
true
,
securityIframe
:
true
,
securityIframe
:
true
,
securityReferrerPolicy
:
true
,
securityReferrerPolicy
:
true
,
...
@@ -286,6 +296,7 @@ export default {
...
@@ -286,6 +296,7 @@ export default {
$authJwtRenewablePeriod: String
$authJwtRenewablePeriod: String
$uploadMaxFileSize: Int
$uploadMaxFileSize: Int
$uploadMaxFiles: Int
$uploadMaxFiles: Int
$uploadScanSVG: Boolean
$securityOpenRedirect: Boolean
$securityOpenRedirect: Boolean
$securityIframe: Boolean
$securityIframe: Boolean
$securityReferrerPolicy: Boolean
$securityReferrerPolicy: Boolean
...
@@ -307,6 +318,7 @@ export default {
...
@@ -307,6 +318,7 @@ export default {
authJwtRenewablePeriod: $authJwtRenewablePeriod,
authJwtRenewablePeriod: $authJwtRenewablePeriod,
uploadMaxFileSize: $uploadMaxFileSize,
uploadMaxFileSize: $uploadMaxFileSize,
uploadMaxFiles: $uploadMaxFiles,
uploadMaxFiles: $uploadMaxFiles,
uploadScanSVG: $uploadScanSVG
securityOpenRedirect: $securityOpenRedirect,
securityOpenRedirect: $securityOpenRedirect,
securityIframe: $securityIframe,
securityIframe: $securityIframe,
securityReferrerPolicy: $securityReferrerPolicy,
securityReferrerPolicy: $securityReferrerPolicy,
...
@@ -337,6 +349,7 @@ export default {
...
@@ -337,6 +349,7 @@ export default {
authJwtRenewablePeriod
:
_
.
get
(
this
.
config
,
'authJwtRenewablePeriod'
,
''
),
authJwtRenewablePeriod
:
_
.
get
(
this
.
config
,
'authJwtRenewablePeriod'
,
''
),
uploadMaxFileSize
:
_
.
toSafeInteger
(
_
.
get
(
this
.
config
,
'uploadMaxFileSize'
,
0
)),
uploadMaxFileSize
:
_
.
toSafeInteger
(
_
.
get
(
this
.
config
,
'uploadMaxFileSize'
,
0
)),
uploadMaxFiles
:
_
.
toSafeInteger
(
_
.
get
(
this
.
config
,
'uploadMaxFiles'
,
0
)),
uploadMaxFiles
:
_
.
toSafeInteger
(
_
.
get
(
this
.
config
,
'uploadMaxFiles'
,
0
)),
uploadScanSVG
:
_
.
get
(
this
.
config
,
'uploadScanSVG'
,
false
),
securityOpenRedirect
:
_
.
get
(
this
.
config
,
'securityOpenRedirect'
,
false
),
securityOpenRedirect
:
_
.
get
(
this
.
config
,
'securityOpenRedirect'
,
false
),
securityIframe
:
_
.
get
(
this
.
config
,
'securityIframe'
,
false
),
securityIframe
:
_
.
get
(
this
.
config
,
'securityIframe'
,
false
),
securityReferrerPolicy
:
_
.
get
(
this
.
config
,
'securityReferrerPolicy'
,
false
),
securityReferrerPolicy
:
_
.
get
(
this
.
config
,
'securityReferrerPolicy'
,
false
),
...
@@ -388,6 +401,7 @@ export default {
...
@@ -388,6 +401,7 @@ export default {
authJwtRenewablePeriod
authJwtRenewablePeriod
uploadMaxFileSize
uploadMaxFileSize
uploadMaxFiles
uploadMaxFiles
uploadScanSVG
securityOpenRedirect
securityOpenRedirect
securityIframe
securityIframe
securityReferrerPolicy
securityReferrerPolicy
...
...
server/app/data.yml
View file @
5d3e8149
...
@@ -80,6 +80,7 @@ defaults:
...
@@ -80,6 +80,7 @@ defaults:
uploads
:
uploads
:
maxFileSize
:
5242880
maxFileSize
:
5242880
maxFiles
:
10
maxFiles
:
10
scanSVG
:
true
flags
:
flags
:
ldapdebug
:
false
ldapdebug
:
false
sqllog
:
false
sqllog
:
false
...
...
server/graph/resolvers/site.js
View file @
5d3e8149
...
@@ -29,7 +29,8 @@ module.exports = {
...
@@ -29,7 +29,8 @@ module.exports = {
authJwtExpiration
:
WIKI
.
config
.
auth
.
tokenExpiration
,
authJwtExpiration
:
WIKI
.
config
.
auth
.
tokenExpiration
,
authJwtRenewablePeriod
:
WIKI
.
config
.
auth
.
tokenRenewal
,
authJwtRenewablePeriod
:
WIKI
.
config
.
auth
.
tokenRenewal
,
uploadMaxFileSize
:
WIKI
.
config
.
uploads
.
maxFileSize
,
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 = {
...
@@ -97,7 +98,8 @@ module.exports = {
WIKI
.
config
.
uploads
=
{
WIKI
.
config
.
uploads
=
{
maxFileSize
:
_
.
get
(
args
,
'uploadMaxFileSize'
,
WIKI
.
config
.
uploads
.
maxFileSize
),
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'
])
await
WIKI
.
configSvc
.
saveToDb
([
'host'
,
'title'
,
'company'
,
'contentLicense'
,
'seo'
,
'logoUrl'
,
'auth'
,
'features'
,
'security'
,
'uploads'
])
...
...
server/graph/schemas/site.graphql
View file @
5d3e8149
...
@@ -54,6 +54,7 @@ type SiteMutation {
...
@@ -54,6 +54,7 @@ type SiteMutation {
securityCSPDirectives
:
String
securityCSPDirectives
:
String
uploadMaxFileSize
:
Int
uploadMaxFileSize
:
Int
uploadMaxFiles
:
Int
uploadMaxFiles
:
Int
uploadScanSVG
:
Boolean
):
DefaultResponse
@
auth
(
requires
:
[
"
manage
:
system
"
])
):
DefaultResponse
@
auth
(
requires
:
[
"
manage
:
system
"
])
}
}
...
@@ -63,15 +64,15 @@ type SiteMutation {
...
@@ -63,15 +64,15 @@ type SiteMutation {
# -----------------------------------------------
# -----------------------------------------------
type
SiteConfig
{
type
SiteConfig
{
host
:
String
!
host
:
String
title
:
String
!
title
:
String
description
:
String
!
description
:
String
robots
:
[
String
]
!
robots
:
[
String
]
analyticsService
:
String
!
analyticsService
:
String
analyticsId
:
String
!
analyticsId
:
String
company
:
String
!
company
:
String
contentLicense
:
String
!
contentLicense
:
String
logoUrl
:
String
!
logoUrl
:
String
authAutoLogin
:
Boolean
authAutoLogin
:
Boolean
authEnforce2FA
:
Boolean
authEnforce2FA
:
Boolean
authHideLocal
:
Boolean
authHideLocal
:
Boolean
...
@@ -79,18 +80,19 @@ type SiteConfig {
...
@@ -79,18 +80,19 @@ type SiteConfig {
authJwtAudience
:
String
authJwtAudience
:
String
authJwtExpiration
:
String
authJwtExpiration
:
String
authJwtRenewablePeriod
:
String
authJwtRenewablePeriod
:
String
featurePageRatings
:
Boolean
!
featurePageRatings
:
Boolean
featurePageComments
:
Boolean
!
featurePageComments
:
Boolean
featurePersonalWikis
:
Boolean
!
featurePersonalWikis
:
Boolean
securityOpenRedirect
:
Boolean
!
securityOpenRedirect
:
Boolean
securityIframe
:
Boolean
!
securityIframe
:
Boolean
securityReferrerPolicy
:
Boolean
!
securityReferrerPolicy
:
Boolean
securityTrustProxy
:
Boolean
!
securityTrustProxy
:
Boolean
securitySRI
:
Boolean
!
securitySRI
:
Boolean
securityHSTS
:
Boolean
!
securityHSTS
:
Boolean
securityHSTSDuration
:
Int
!
securityHSTSDuration
:
Int
securityCSP
:
Boolean
!
securityCSP
:
Boolean
securityCSPDirectives
:
String
!
securityCSPDirectives
:
String
uploadMaxFileSize
:
Int
!
uploadMaxFileSize
:
Int
uploadMaxFiles
:
Int
!
uploadMaxFiles
:
Int
uploadScanSVG
:
Boolean
}
}
server/jobs/sanitize-svg.js
0 → 100644
View file @
5d3e8149
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
}
}
server/models/assets.js
View file @
5d3e8149
...
@@ -99,6 +99,16 @@ module.exports = class Asset extends Model {
...
@@ -99,6 +99,16 @@ module.exports = class Asset extends Model {
folderId
:
opts
.
folderId
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
// Save asset data
try
{
try
{
const
fileBuffer
=
await
fs
.
readFile
(
opts
.
path
)
const
fileBuffer
=
await
fs
.
readFile
(
opts
.
path
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment