@ditojs/server 2.87.0 → 2.88.0
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/package.json +2 -2
- package/src/app/Application.js +5 -2
- package/src/storage/Storage.js +32 -23
- package/src/utils/asset.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.88.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/main/packages/server",
|
|
@@ -90,5 +90,5 @@
|
|
|
90
90
|
"objection": "^3.1.5",
|
|
91
91
|
"typescript": "^5.9.3"
|
|
92
92
|
},
|
|
93
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "1d413e8528f8a40e3cc7950db62ebeb4d687ce67"
|
|
94
94
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -40,6 +40,7 @@ import { Service } from '../services/index.js'
|
|
|
40
40
|
import { Storage } from '../storage/index.js'
|
|
41
41
|
import { convertSchema } from '../schema/index.js'
|
|
42
42
|
import { getDuration, subtractDuration } from '../utils/duration.js'
|
|
43
|
+
import { resolveFileUrl } from '../utils/asset.js'
|
|
43
44
|
import {
|
|
44
45
|
ResponseError,
|
|
45
46
|
ValidationError,
|
|
@@ -997,8 +998,7 @@ export class Application extends Koa {
|
|
|
997
998
|
}...`
|
|
998
999
|
)
|
|
999
1000
|
if (url.startsWith('file://')) {
|
|
1000
|
-
|
|
1001
|
-
data = await fs.readFile(filepath)
|
|
1001
|
+
data = await fs.readFile(new URL(resolveFileUrl(url)))
|
|
1002
1002
|
} else {
|
|
1003
1003
|
const response = await fetch(url)
|
|
1004
1004
|
const arrayBuffer = await response.arrayBuffer()
|
|
@@ -1007,6 +1007,9 @@ export class Application extends Koa {
|
|
|
1007
1007
|
}
|
|
1008
1008
|
}
|
|
1009
1009
|
const importedFile = await storage.addFile(file, data)
|
|
1010
|
+
// Sign the imported foreign file so it passes verification when
|
|
1011
|
+
// createAssets() triggers $parseJson() → convertAssetFile().
|
|
1012
|
+
storage.signAssetFile(importedFile)
|
|
1010
1013
|
await this.createAssets(storage, [importedFile], 0, transaction)
|
|
1011
1014
|
importedFiles.push(importedFile)
|
|
1012
1015
|
// Merge back the changed file properties into the actual file
|
package/src/storage/Storage.js
CHANGED
|
@@ -8,6 +8,7 @@ import { readMediaAttributes } from 'leather'
|
|
|
8
8
|
import { hyphenate, toPromiseCallback } from '@ditojs/utils'
|
|
9
9
|
import { AssetFile } from './AssetFile.js'
|
|
10
10
|
import { AssetError } from '../errors/AssetError.js'
|
|
11
|
+
import { resolveFileUrl } from '../utils/asset.js'
|
|
11
12
|
|
|
12
13
|
const storageClasses = {}
|
|
13
14
|
|
|
@@ -75,19 +76,23 @@ export class Storage {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
isImportSourceAllowed(url) {
|
|
78
|
-
return picomatch.isMatch(
|
|
79
|
+
return picomatch.isMatch(
|
|
80
|
+
resolveFileUrl(url),
|
|
81
|
+
(this.config.allowedImports || []).map(resolveFileUrl)
|
|
82
|
+
)
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
convertAssetFile(file, { trusted = false } = {}) {
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
if (!trusted) {
|
|
87
|
+
if (this.isImportSourceAllowed(file.url)) {
|
|
88
|
+
this.signAssetFile(file)
|
|
89
|
+
} else if (!this.verifyAssetFile(file)) {
|
|
90
|
+
throw new AssetError(
|
|
91
|
+
`Invalid asset signature for file '${
|
|
92
|
+
file.name ?? file.key
|
|
93
|
+
}'`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
97
|
AssetFile.convert(file, this)
|
|
93
98
|
}
|
|
@@ -141,6 +146,23 @@ export class Storage {
|
|
|
141
146
|
return this._getFileUrl(file)
|
|
142
147
|
}
|
|
143
148
|
|
|
149
|
+
signAssetFile(file) {
|
|
150
|
+
file.signature = this._signAssetKey(file.key)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
verifyAssetFile(file) {
|
|
154
|
+
if (file) {
|
|
155
|
+
const expected = this._signAssetKey(file.key)
|
|
156
|
+
try {
|
|
157
|
+
return crypto.timingSafeEqual(
|
|
158
|
+
Buffer.from(expected, 'hex'),
|
|
159
|
+
Buffer.from(file.signature, 'hex')
|
|
160
|
+
)
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
163
|
+
return false
|
|
164
|
+
}
|
|
165
|
+
|
|
144
166
|
_signAssetKey(key) {
|
|
145
167
|
const secret = (
|
|
146
168
|
this.app.keys?.[0] ??
|
|
@@ -152,19 +174,6 @@ export class Storage {
|
|
|
152
174
|
.digest('hex')
|
|
153
175
|
}
|
|
154
176
|
|
|
155
|
-
_verifyAssetKey(key, signature) {
|
|
156
|
-
if (!key || !signature) return false
|
|
157
|
-
const expected = this._signAssetKey(key)
|
|
158
|
-
try {
|
|
159
|
-
return crypto.timingSafeEqual(
|
|
160
|
-
Buffer.from(expected, 'hex'),
|
|
161
|
-
Buffer.from(signature, 'hex')
|
|
162
|
-
)
|
|
163
|
-
} catch {
|
|
164
|
-
return false
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
177
|
_getUrl(...parts) {
|
|
169
178
|
return this.url
|
|
170
179
|
? new URL(path.posix.join(...parts), this.url).toString()
|