@ditojs/server 2.87.0 → 2.89.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "2.87.0",
3
+ "version": "2.89.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",
@@ -25,10 +25,10 @@
25
25
  "node >= 18"
26
26
  ],
27
27
  "dependencies": {
28
- "@ditojs/admin": "^2.87.0",
29
- "@ditojs/build": "^2.87.0",
30
- "@ditojs/router": "^2.87.0",
31
- "@ditojs/utils": "^2.87.0",
28
+ "@ditojs/admin": "^2.89.0",
29
+ "@ditojs/build": "^2.89.0",
30
+ "@ditojs/router": "^2.89.0",
31
+ "@ditojs/utils": "^2.89.0",
32
32
  "@koa/cors": "^5.0.0",
33
33
  "@koa/etag": "^5.0.2",
34
34
  "@koa/multer": "^4.0.0",
@@ -90,5 +90,5 @@
90
90
  "objection": "^3.1.5",
91
91
  "typescript": "^5.9.3"
92
92
  },
93
- "gitHead": "cbd05e604a1d212dfc91f9686b43bef75815a409"
93
+ "gitHead": "9978921be041dd74026e5c8cc4e889f4ff3303c7"
94
94
  }
@@ -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
- const filepath = path.resolve(url.substring(7))
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
@@ -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(url, this.config.allowedImports || [])
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
- !trusted &&
84
- !this._verifyAssetKey(file.key, file.signature)
85
- ) {
86
- throw new AssetError(
87
- `Invalid asset signature for file '${
88
- file.name ?? file.key
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()
@@ -0,0 +1,7 @@
1
+ import path from 'path'
2
+
3
+ export function resolveFileUrl(url) {
4
+ return url.startsWith('file://')
5
+ ? `file://${path.resolve(url.slice(7))}`
6
+ : url
7
+ }