solid-server 5.7.7 → 5.7.9-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +2 -2
- package/README.md +2 -0
- package/lib/acl-checker.js +8 -1
- package/lib/handlers/allow.js +4 -3
- package/lib/handlers/patch.js +6 -4
- package/lib/handlers/put.js +46 -5
- package/lib/header.js +1 -1
- package/lib/ldp-middleware.js +1 -1
- package/lib/ldp.js +46 -47
- package/package.json +15 -15
package/.github/workflows/ci.yml
CHANGED
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
|
|
18
18
|
strategy:
|
|
19
19
|
matrix:
|
|
20
|
-
node-version: [
|
|
20
|
+
node-version: [16.x, 18.x]
|
|
21
21
|
os: [ubuntu-latest]
|
|
22
22
|
|
|
23
23
|
steps:
|
|
@@ -97,4 +97,4 @@ jobs:
|
|
|
97
97
|
build-args: SOLID_SERVER_VERSION=${{ steps.tagName.outputs.version }}
|
|
98
98
|
push: true
|
|
99
99
|
tags: ${{ steps.meta.outputs.tags }}
|
|
100
|
-
labels: ${{ steps.meta.outputs.labels }}
|
|
100
|
+
labels: ${{ steps.meta.outputs.labels }}
|
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
`solid-server` lets you run a Solid server on top of the file-system. You can use it as a [command-line tool](https://github.com/solid/node-solid-server/blob/master/README.md#command-line-usage) (easy) or as a [library](https://github.com/solid/node-solid-server/blob/master/README.md#library-usage) (advanced).
|
|
11
11
|
|
|
12
|
+
The [solid test suite](https://github.com/nodeSolidServer/node-solid-server/blob/main/test/surface/run-solid-test-suite.sh) runs as part of GitHub Actions on this repository, ensuring that this server is always (to the best of our knowledge) fully spec compliant.
|
|
13
|
+
|
|
12
14
|
## Solid Features supported
|
|
13
15
|
- [x] [Linked Data Platform](http://www.w3.org/TR/ldp/)
|
|
14
16
|
- [x] [Web Access Control](http://www.w3.org/wiki/WebAccessControl)
|
package/lib/acl-checker.js
CHANGED
|
@@ -28,7 +28,14 @@ class ACLChecker {
|
|
|
28
28
|
constructor (resource, options = {}) {
|
|
29
29
|
this.resource = resource
|
|
30
30
|
this.resourceUrl = new URL(resource)
|
|
31
|
-
this.agentOrigin =
|
|
31
|
+
this.agentOrigin = null
|
|
32
|
+
try {
|
|
33
|
+
if (options.strictOrigin && options.agentOrigin) {
|
|
34
|
+
this.agentOrigin = rdf.sym(options.agentOrigin)
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
// noop
|
|
38
|
+
}
|
|
32
39
|
this.fetch = options.fetch
|
|
33
40
|
this.fetchGraph = options.fetchGraph
|
|
34
41
|
this.trustedOrigins = options.strictOrigin && options.trustedOrigins ? options.trustedOrigins.map(trustedOrigin => rdf.sym(trustedOrigin)) : null
|
package/lib/handlers/allow.js
CHANGED
|
@@ -72,9 +72,10 @@ function allow (mode) {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// check user is owner.
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
// check if user is owner. Check isOwner from /.meta
|
|
76
|
+
try {
|
|
77
|
+
if (resourceUrl.endsWith('.acl') && (await ldp.isOwner(userId, req.hostname))) return next()
|
|
78
|
+
} catch (err) {}
|
|
78
79
|
const error = req.authError || await req.acl.getError(userId, mode)
|
|
79
80
|
debug(`${mode} access denied to ${userId || '(none)'}: ${error.status} - ${error.message}`)
|
|
80
81
|
next(error)
|
package/lib/handlers/patch.js
CHANGED
|
@@ -53,7 +53,11 @@ async function patchHandler (req, res, next) {
|
|
|
53
53
|
({ path, contentType } = await ldp.resourceMapper.mapUrlToFile(
|
|
54
54
|
{ url: req, createIfNotExists: true, contentType: contentTypeForNew(req) }))
|
|
55
55
|
// check if a folder with same name exists
|
|
56
|
-
|
|
56
|
+
try {
|
|
57
|
+
await ldp.checkItemName(req)
|
|
58
|
+
} catch (err) {
|
|
59
|
+
return next(err)
|
|
60
|
+
}
|
|
57
61
|
resourceExists = false
|
|
58
62
|
}
|
|
59
63
|
const { url } = await ldp.resourceMapper.mapFileToUrl({ path, hostname: req.hostname })
|
|
@@ -143,8 +147,6 @@ async function checkPermission (request, patchObject, resourceExists) {
|
|
|
143
147
|
// Now that we know the details of the patch,
|
|
144
148
|
// we might need to perform additional checks.
|
|
145
149
|
let modes = []
|
|
146
|
-
// acl:default Write is required for create
|
|
147
|
-
if (!resourceExists) modes = ['Write']
|
|
148
150
|
const { acl, session: { userId } } = request
|
|
149
151
|
// Read access is required for DELETE and WHERE.
|
|
150
152
|
// If we would allows users without read access,
|
|
@@ -164,7 +166,7 @@ async function checkPermission (request, patchObject, resourceExists) {
|
|
|
164
166
|
if (!allAllowed) {
|
|
165
167
|
// check owner with Control
|
|
166
168
|
const ldp = request.app.locals.ldp
|
|
167
|
-
if (request.path.endsWith('.acl') &&
|
|
169
|
+
if (request.path.endsWith('.acl') && await ldp.isOwner(userId, request.hostname)) return Promise.resolve(patchObject)
|
|
168
170
|
|
|
169
171
|
const errors = await Promise.all(modes.map(mode => acl.getError(userId, mode)))
|
|
170
172
|
const error = errors.filter(error => !!error)
|
package/lib/handlers/put.js
CHANGED
|
@@ -9,10 +9,17 @@ const { stringToStream } = require('../utils')
|
|
|
9
9
|
async function handler (req, res, next) {
|
|
10
10
|
debug(req.originalUrl)
|
|
11
11
|
res.header('MS-Author-Via', 'SPARQL')
|
|
12
|
-
|
|
13
12
|
const contentType = req.get('content-type')
|
|
13
|
+
|
|
14
|
+
// check if a folder or resource with same name exists
|
|
15
|
+
try {
|
|
16
|
+
const ldp = req.app.locals.ldp
|
|
17
|
+
await ldp.checkItemName(req)
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return next(e)
|
|
20
|
+
}
|
|
14
21
|
// check for valid rdf content for auxiliary resource and /profile/card
|
|
15
|
-
//
|
|
22
|
+
// TODO check that /profile/card is a minimal valid WebID card
|
|
16
23
|
if (isAuxiliary(req) || req.originalUrl === '/profile/card') {
|
|
17
24
|
if (contentType === 'text/turtle') {
|
|
18
25
|
return bodyParser.text({ type: () => true })(req, res, () => putValidRdf(req, res, next))
|
|
@@ -21,17 +28,51 @@ async function handler (req, res, next) {
|
|
|
21
28
|
return putStream(req, res, next)
|
|
22
29
|
}
|
|
23
30
|
|
|
31
|
+
// Verifies whether the user is allowed to perform Append PUT on the target
|
|
32
|
+
async function checkPermission (request, resourceExists) {
|
|
33
|
+
// If no ACL object was passed down, assume permissions are okay.
|
|
34
|
+
if (!request.acl) return Promise.resolve()
|
|
35
|
+
// At this point, we already assume append access,
|
|
36
|
+
// we might need to perform additional checks.
|
|
37
|
+
let modes = []
|
|
38
|
+
// acl:default Write is required for PUT when Resource Exists
|
|
39
|
+
if (resourceExists) modes = ['Write']
|
|
40
|
+
// if (resourceExists && request.originalUrl.endsWith('.acl')) modes = ['Control']
|
|
41
|
+
const { acl, session: { userId } } = request
|
|
42
|
+
|
|
43
|
+
const allowed = await Promise.all(modes.map(mode => acl.can(userId, mode, request.method, resourceExists)))
|
|
44
|
+
const allAllowed = allowed.reduce((memo, allowed) => memo && allowed, true)
|
|
45
|
+
if (!allAllowed) {
|
|
46
|
+
// check owner with Control
|
|
47
|
+
// const ldp = request.app.locals.ldp
|
|
48
|
+
// if (request.path.endsWith('.acl') && userId === await ldp.getOwner(request.hostname)) return Promise.resolve()
|
|
49
|
+
|
|
50
|
+
const errors = await Promise.all(modes.map(mode => acl.getError(userId, mode)))
|
|
51
|
+
const error = errors.filter(error => !!error)
|
|
52
|
+
.reduce((prevErr, err) => prevErr.status > err.status ? prevErr : err, { status: 0 })
|
|
53
|
+
return Promise.reject(error)
|
|
54
|
+
}
|
|
55
|
+
return Promise.resolve()
|
|
56
|
+
}
|
|
57
|
+
|
|
24
58
|
// TODO could be renamed as putResource (it now covers container and non-container)
|
|
25
59
|
async function putStream (req, res, next, stream = req) {
|
|
26
60
|
const ldp = req.app.locals.ldp
|
|
61
|
+
// try {
|
|
62
|
+
// Obtain details of the target resource
|
|
63
|
+
let resourceExists = true
|
|
64
|
+
try {
|
|
65
|
+
// First check if the file already exists
|
|
66
|
+
await ldp.resourceMapper.mapUrlToFile({ url: req })
|
|
67
|
+
} catch (err) {
|
|
68
|
+
resourceExists = false
|
|
69
|
+
}
|
|
27
70
|
try {
|
|
28
|
-
|
|
71
|
+
if (!req.originalUrl.endsWith('.acl')) await checkPermission(req, resourceExists)
|
|
29
72
|
await ldp.put(req, stream, getContentType(req.headers))
|
|
30
|
-
debug('succeded putting the file/folder')
|
|
31
73
|
res.sendStatus(201)
|
|
32
74
|
return next()
|
|
33
75
|
} catch (err) {
|
|
34
|
-
debug('error putting the file/folder:' + err.message)
|
|
35
76
|
err.message = 'Can\'t write file/folder: ' + err.message
|
|
36
77
|
return next(err)
|
|
37
78
|
}
|
package/lib/header.js
CHANGED
|
@@ -128,7 +128,7 @@ async function addPermissions (req, res, next) {
|
|
|
128
128
|
getPermissionsFor(acl, null, req),
|
|
129
129
|
getPermissionsFor(acl, session.userId, req)
|
|
130
130
|
])
|
|
131
|
-
if (resource.endsWith('.acl') && userPerms === '' &&
|
|
131
|
+
if (resource.endsWith('.acl') && userPerms === '' && await ldp.isOwner(session.userId, req.hostname)) userPerms = 'control'
|
|
132
132
|
debug.ACL(`Permissions on ${resource} for ${session.userId || '(none)'}: ${userPerms}`)
|
|
133
133
|
debug.ACL(`Permissions on ${resource} for public: ${publicPerms}`)
|
|
134
134
|
res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
|
package/lib/ldp-middleware.js
CHANGED
|
@@ -25,7 +25,7 @@ function LdpMiddleware (corsSettings) {
|
|
|
25
25
|
router.get('/*', index, allow('Read'), header.addPermissions, get)
|
|
26
26
|
router.post('/*', allow('Append'), post)
|
|
27
27
|
router.patch('/*', allow('Append'), patch)
|
|
28
|
-
router.put('/*', allow('
|
|
28
|
+
router.put('/*', allow('Append'), put)
|
|
29
29
|
router.delete('/*', allow('Write'), del)
|
|
30
30
|
|
|
31
31
|
return router
|
package/lib/ldp.js
CHANGED
|
@@ -157,37 +157,13 @@ class LDP {
|
|
|
157
157
|
if (container) {
|
|
158
158
|
extension = ''
|
|
159
159
|
}
|
|
160
|
-
// Check for file or folder with same name
|
|
161
|
-
let testName, fileName
|
|
162
|
-
try {
|
|
163
|
-
// check for defaulted slug in NSS POST (slug with extension)
|
|
164
|
-
fileName = slug.endsWith(extension) || slug.endsWith(this.suffixAcl) || slug.endsWith(this.suffixMeta) ? slug : slug + extension
|
|
165
|
-
fileName = container ? fileName : fileName + '/'
|
|
166
|
-
const { url: testUrl } = await this.resourceMapper.mapFileToUrl({ hostname, path: containerPath + fileName })
|
|
167
|
-
const { path: testPath } = await this.resourceMapper.mapUrlToFile({ url: testUrl })
|
|
168
|
-
testName = container ? fs.lstatSync(testPath).isFile() : fs.lstatSync(testPath).isDirectory()
|
|
169
|
-
} catch (err) { testName = false }
|
|
170
|
-
|
|
171
|
-
if (testName) {
|
|
172
|
-
throw error(404, 'Container and resource cannot have the same name in URI')
|
|
173
|
-
}
|
|
174
160
|
|
|
175
|
-
//
|
|
176
|
-
|
|
161
|
+
// allways return a valid URL.
|
|
162
|
+
const resourceUrl = await ldp.getAvailableUrl(hostname, containerPath, { slug, extension, container })
|
|
177
163
|
debug.handlers('POST -- Will create at: ' + resourceUrl)
|
|
178
|
-
let originalUrl = resourceUrl
|
|
179
|
-
|
|
180
|
-
if (container) {
|
|
181
|
-
// Create directory by an LDP PUT to the container's .meta resource
|
|
182
|
-
resourceUrl = `${resourceUrl}${resourceUrl.endsWith('/') ? '' : '/'}` // ${ldp.suffixMeta}`
|
|
183
|
-
if (originalUrl && !originalUrl.endsWith('/')) {
|
|
184
|
-
originalUrl += '/'
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
// const { url: putUrl } = await this.resourceMapper.mapFileToUrl({ path: resourceUrl, hostname })
|
|
188
164
|
|
|
189
165
|
await ldp.put(resourceUrl, stream, contentType)
|
|
190
|
-
return URL.parse(
|
|
166
|
+
return URL.parse(resourceUrl).path
|
|
191
167
|
}
|
|
192
168
|
|
|
193
169
|
isAuxResource (slug, extension) {
|
|
@@ -243,8 +219,11 @@ class LDP {
|
|
|
243
219
|
'PUT request requires a content-type via the Content-Type header')
|
|
244
220
|
}
|
|
245
221
|
|
|
246
|
-
//
|
|
247
|
-
|
|
222
|
+
// reject resource with percent-encoded $ extension
|
|
223
|
+
const dollarExtensionRegex = /%(?:24)\.[^%(?:24)]*$/ // /\$\.[^$]*$/
|
|
224
|
+
if ((url.url || url).match(dollarExtensionRegex)) {
|
|
225
|
+
throw error(400, 'Resource with a $.ext is not allowed by the server')
|
|
226
|
+
}
|
|
248
227
|
|
|
249
228
|
// First check if we are above quota
|
|
250
229
|
let isOverQuota
|
|
@@ -348,19 +327,30 @@ class LDP {
|
|
|
348
327
|
} catch (err) { }
|
|
349
328
|
}
|
|
350
329
|
|
|
330
|
+
// check if a document (or container) has the same name than a document (or container)
|
|
351
331
|
async checkItemName (url) {
|
|
352
|
-
let testName
|
|
353
|
-
const
|
|
332
|
+
let testName, testPath
|
|
333
|
+
const { hostname, pathname } = this.resourceMapper._parseUrl(url) // (url.url || url)
|
|
334
|
+
let itemUrl = this.resourceMapper.resolveUrl(hostname, pathname)
|
|
354
335
|
const container = itemUrl.endsWith('/')
|
|
355
336
|
try {
|
|
356
337
|
const testUrl = container ? itemUrl.slice(0, -1) : itemUrl + '/'
|
|
357
338
|
const { path: testPath } = await this.resourceMapper.mapUrlToFile({ url: testUrl })
|
|
358
|
-
// testName = fs.lstatSync(testPath).isDirectory()
|
|
359
339
|
testName = container ? fs.lstatSync(testPath).isFile() : fs.lstatSync(testPath).isDirectory()
|
|
360
|
-
} catch (err) {
|
|
361
|
-
|
|
340
|
+
} catch (err) {
|
|
341
|
+
testName = false
|
|
342
|
+
|
|
343
|
+
// item does not exist, check one level up the tree
|
|
344
|
+
if (itemUrl.endsWith('/')) itemUrl = itemUrl.substring(0, itemUrl.length - 1)
|
|
345
|
+
itemUrl = itemUrl.substring(0, itemUrl.lastIndexOf('/') + 1)
|
|
346
|
+
const { pathname } = this.resourceMapper._parseUrl(itemUrl) // (url.url || url)
|
|
347
|
+
// check not at root
|
|
348
|
+
if (pathname !== '/') {
|
|
349
|
+
return await this.checkItemName(itemUrl)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
362
352
|
if (testName) {
|
|
363
|
-
throw error(
|
|
353
|
+
throw error(404, `${testPath}: Container and resource cannot have the same name in URI`)
|
|
364
354
|
}
|
|
365
355
|
}
|
|
366
356
|
|
|
@@ -437,7 +427,7 @@ class LDP {
|
|
|
437
427
|
// this is a hack to replace solid:owner, using solid:account in /.meta to avoid NSS migration
|
|
438
428
|
// this /.meta has no functionality in actual NSS
|
|
439
429
|
// comment https://github.com/solid/node-solid-server/pull/1604#discussion_r652903546
|
|
440
|
-
async
|
|
430
|
+
async isOwner (webId, hostname) {
|
|
441
431
|
// const ldp = req.app.locals.ldp
|
|
442
432
|
const rootUrl = this.resourceMapper.resolveUrl(hostname)
|
|
443
433
|
let graph
|
|
@@ -445,8 +435,8 @@ class LDP {
|
|
|
445
435
|
// TODO check for permission ?? Owner is a MUST
|
|
446
436
|
graph = await this.getGraph(rootUrl + '/.meta')
|
|
447
437
|
const SOLID = $rdf.Namespace('http://www.w3.org/ns/solid/terms#')
|
|
448
|
-
const owner = await graph.
|
|
449
|
-
return owner.
|
|
438
|
+
const owner = await graph.statementsMatching($rdf.sym(webId), SOLID('account'), $rdf.sym(rootUrl + '/'))
|
|
439
|
+
return owner.length
|
|
450
440
|
} catch (error) {
|
|
451
441
|
throw new Error(`Failed to get owner from ${rootUrl}/.meta, got ` + error)
|
|
452
442
|
}
|
|
@@ -589,17 +579,26 @@ class LDP {
|
|
|
589
579
|
}
|
|
590
580
|
}
|
|
591
581
|
|
|
592
|
-
async getAvailableUrl (hostname, containerURI, { slug = uuid.v1(), extension }) {
|
|
582
|
+
async getAvailableUrl (hostname, containerURI, { slug = uuid.v1(), extension, container }) {
|
|
593
583
|
let requestUrl = this.resourceMapper.resolveUrl(hostname, containerURI)
|
|
594
|
-
requestUrl = requestUrl.replace(/\/*$/, '/')
|
|
584
|
+
requestUrl = requestUrl.replace(/\/*$/, '/') // ??? what for
|
|
595
585
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
586
|
+
let itemName = slug.endsWith(extension) || slug.endsWith(this.suffixAcl) || slug.endsWith(this.suffixMeta) ? slug : slug + extension
|
|
587
|
+
try {
|
|
588
|
+
// check resource exists
|
|
589
|
+
const context = container ? '/' : ''
|
|
590
|
+
await this.resourceMapper.mapUrlToFile({ url: (requestUrl + itemName + context) })
|
|
591
|
+
itemName = `${uuid.v1()}-${itemName}`
|
|
592
|
+
} catch (e) {
|
|
593
|
+
try {
|
|
594
|
+
// check resource with same name exists
|
|
595
|
+
const context = !container ? '/' : ''
|
|
596
|
+
await this.resourceMapper.mapUrlToFile({ url: (requestUrl + itemName + context) })
|
|
597
|
+
itemName = `${uuid.v1()}-${itemName}`
|
|
598
|
+
} catch (e) {}
|
|
599
|
+
}
|
|
600
|
+
if (container) itemName += '/'
|
|
601
|
+
return requestUrl + itemName
|
|
603
602
|
}
|
|
604
603
|
|
|
605
604
|
getTrustedOrigins (req) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-server",
|
|
3
3
|
"description": "Solid server on top of the file-system",
|
|
4
|
-
"version": "5.7.
|
|
4
|
+
"version": "5.7.9-alpha",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Tim Berners-Lee",
|
|
7
7
|
"email": "timbl@w3.org"
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"cached-path-relative": "^1.1.0",
|
|
70
70
|
"camelize": "^1.0.1",
|
|
71
71
|
"cheerio": "^1.0.0-rc.12",
|
|
72
|
-
"colorette": "^2.0.
|
|
72
|
+
"colorette": "^2.0.20",
|
|
73
73
|
"commander": "^8.3.0",
|
|
74
74
|
"cors": "^2.8.5",
|
|
75
75
|
"debug": "^4.3.4",
|
|
@@ -82,27 +82,27 @@
|
|
|
82
82
|
"get-folder-size": "^2.0.1",
|
|
83
83
|
"glob": "^7.2.3",
|
|
84
84
|
"global-tunnel-ng": "^2.7.1",
|
|
85
|
-
"handlebars": "^4.7.
|
|
85
|
+
"handlebars": "^4.7.8",
|
|
86
86
|
"http-proxy-middleware": "^2.0.6",
|
|
87
|
-
"inquirer": "^8.2.
|
|
87
|
+
"inquirer": "^8.2.6",
|
|
88
88
|
"into-stream": "^6.0.0",
|
|
89
89
|
"ip-range-check": "0.2.0",
|
|
90
90
|
"is-ip": "^3.1.0",
|
|
91
91
|
"li": "^1.3.0",
|
|
92
|
-
"mashlib": "^1.8.
|
|
92
|
+
"mashlib": "^1.8.9",
|
|
93
93
|
"mime-types": "^2.1.35",
|
|
94
94
|
"negotiator": "^0.6.3",
|
|
95
|
-
"node-fetch": "^2.
|
|
95
|
+
"node-fetch": "^2.7.0",
|
|
96
96
|
"node-forge": "^1.3.1",
|
|
97
97
|
"node-mailer": "^0.1.1",
|
|
98
|
-
"nodemailer": "^6.9.
|
|
98
|
+
"nodemailer": "^6.9.7",
|
|
99
99
|
"oidc-op-express": "^0.0.3",
|
|
100
100
|
"owasp-password-strength-test": "^1.3.0",
|
|
101
101
|
"recursive-readdir": "^2.2.3",
|
|
102
102
|
"request": "^2.88.2",
|
|
103
103
|
"rimraf": "^3.0.2",
|
|
104
104
|
"solid-auth-client": "^2.5.6",
|
|
105
|
-
"solid-namespace": "^0.5.
|
|
105
|
+
"solid-namespace": "^0.5.3",
|
|
106
106
|
"solid-ws": "^0.4.3",
|
|
107
107
|
"text-encoder-lite": "^2.0.0",
|
|
108
108
|
"the-big-username-blacklist": "^1.5.2",
|
|
@@ -110,26 +110,26 @@
|
|
|
110
110
|
"urijs": "^1.19.11",
|
|
111
111
|
"uuid": "^8.3.2",
|
|
112
112
|
"valid-url": "^1.0.9",
|
|
113
|
-
"validator": "^13.
|
|
113
|
+
"validator": "^13.11.0",
|
|
114
114
|
"vhost": "^3.0.2"
|
|
115
115
|
},
|
|
116
116
|
"devDependencies": {
|
|
117
|
-
"@solid/solid-auth-oidc": "
|
|
118
|
-
"chai": "^4.3.
|
|
117
|
+
"@solid/solid-auth-oidc": "0.3.0",
|
|
118
|
+
"chai": "^4.3.10",
|
|
119
119
|
"chai-as-promised": "7.1.1",
|
|
120
120
|
"cross-env": "7.0.3",
|
|
121
121
|
"dirty-chai": "2.0.1",
|
|
122
122
|
"eslint": "^7.32.0",
|
|
123
123
|
"localstorage-memory": "1.0.3",
|
|
124
|
-
"mocha": "^
|
|
125
|
-
"nock": "^13.
|
|
126
|
-
"node-mocks-http": "^1.
|
|
124
|
+
"mocha": "^10.2.0",
|
|
125
|
+
"nock": "^13.4.0",
|
|
126
|
+
"node-mocks-http": "^1.14.0",
|
|
127
127
|
"nyc": "15.1.0",
|
|
128
128
|
"pre-commit": "1.2.2",
|
|
129
129
|
"randombytes": "2.1.0",
|
|
130
130
|
"sinon": "12.0.1",
|
|
131
131
|
"sinon-chai": "3.7.0",
|
|
132
|
-
"snyk": "^1.
|
|
132
|
+
"snyk": "^1.1264.0",
|
|
133
133
|
"standard": "16.0.4",
|
|
134
134
|
"supertest": "^6.3.3",
|
|
135
135
|
"turtle-validator": "1.1.1",
|