Skip to content

fix: AssetPacks router#798

Open
cyaiox wants to merge 3 commits intomasterfrom
fix/asset-packs-router
Open

fix: AssetPacks router#798
cyaiox wants to merge 3 commits intomasterfrom
fix/asset-packs-router

Conversation

@cyaiox
Copy link
Member

@cyaiox cyaiox commented Mar 11, 2026

This PR fixes the upserting of assets' parent IDs to another parent.

Additionally, adds auth to the assets endpoint.

cyaiox added 3 commits March 10, 2026 13:07
Force asset_pack_id to match the URL parameter instead of trusting
the client-supplied value in the request body.
Add authentication and ownership checks to GET /assets and GET /assets/:id.
Assets are now filtered by the caller's address or the default address,
preventing cross-user asset discovery.
this.router.get(
'/assets/:id',
withCors,
withAuthentication,

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 1 day ago

In general, the fix is to introduce a rate-limiting middleware (for example, using express-rate-limit) and apply it to the sensitive routes that perform authentication/authorization and database access. This middleware should be placed in the route definition before server.handleRequest(...) so that obviously abusive traffic is rejected early, without hitting the application logic or database.

In this file, the best approach without changing existing functionality is:

  • Import a rate-limiting middleware implementation (e.g., express-rate-limit).
  • Define a limiter instance configured for these asset routes (for example, allowing a reasonable number of requests per 15-minute window).
  • Apply this limiter to the /assets/:id and /assets GET routes by inserting it into the middleware chain after withCors and withAuthentication but before withAssetExists / server.handleRequest(...).
    This preserves existing behavior, adds only rejection in case of too many requests, and ensures that authenticated users are each rate-limited according to the configuration (or, if using IP-based default, at least globally throttled).

Concretely, within src/Asset/Asset.router.ts:

  • Add import rateLimit from 'express-rate-limit' to the import section.
  • Inside mount(), define a const assetRateLimiter = rateLimit({...}).
  • Update the two this.router.get(...) blocks for /assets/:id and /assets to include assetRateLimiter in their middleware lists.
Suggested changeset 2
src/Asset/Asset.router.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Asset/Asset.router.ts b/src/Asset/Asset.router.ts
--- a/src/Asset/Asset.router.ts
+++ b/src/Asset/Asset.router.ts
@@ -13,6 +13,7 @@
 import { getDefaultEthAddress } from '../AssetPack/utils'
 import { Asset } from './Asset.model'
 import { withAuthentication, AuthRequest } from '../middleware/authentication'
+import rateLimit from 'express-rate-limit'
 
 const DEFAULT_ETH_ADDRESS = getDefaultEthAddress()
 
@@ -24,6 +25,10 @@
       AssetPack,
       'assetPackId'
     )
+    const assetRateLimiter = rateLimit({
+      windowMs: 15 * 60 * 1000, // 15 minutes
+      max: 100, // limit each IP to 100 requests per windowMs for asset routes
+    })
 
     /**
      * CORS for the OPTIONS header
@@ -58,6 +63,7 @@
       '/assets/:id',
       withCors,
       withAuthentication,
+      assetRateLimiter,
       withAssetExists,
       server.handleRequest(this.getAsset)
     )
@@ -69,6 +75,7 @@
       '/assets',
       withCors,
       withAuthentication,
+      assetRateLimiter,
       server.handleRequest(this.getAssets)
     )
   }
EOF
@@ -13,6 +13,7 @@
import { getDefaultEthAddress } from '../AssetPack/utils'
import { Asset } from './Asset.model'
import { withAuthentication, AuthRequest } from '../middleware/authentication'
import rateLimit from 'express-rate-limit'

const DEFAULT_ETH_ADDRESS = getDefaultEthAddress()

@@ -24,6 +25,10 @@
AssetPack,
'assetPackId'
)
const assetRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs for asset routes
})

/**
* CORS for the OPTIONS header
@@ -58,6 +63,7 @@
'/assets/:id',
withCors,
withAuthentication,
assetRateLimiter,
withAssetExists,
server.handleRequest(this.getAsset)
)
@@ -69,6 +75,7 @@
'/assets',
withCors,
withAuthentication,
assetRateLimiter,
server.handleRequest(this.getAssets)
)
}
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -73,7 +73,8 @@
     "prom-client": "^13.1.0",
     "pull-stream": "^3.6.12",
     "tslint": "^6.1.3",
-    "uuid": "^8.3.2"
+    "uuid": "^8.3.2",
+    "express-rate-limit": "^8.3.1"
   },
   "devDependencies": {
     "@swc/core": "^1.2.102",
EOF
@@ -73,7 +73,8 @@
"prom-client": "^13.1.0",
"pull-stream": "^3.6.12",
"tslint": "^6.1.3",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"express-rate-limit": "^8.3.1"
},
"devDependencies": {
"@swc/core": "^1.2.102",
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 8.3.1 None
Copilot is powered by AI and may make mistakes. Always verify output.
this.router.get(
'/assets',
withCors,
withAuthentication,

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 1 day ago

In general, to fix missing rate limiting on an Express route, you add a rate-limiting middleware (for example using express-rate-limit) and plug it into the handler chain before the expensive operation. This limits how many requests a client can make to that endpoint in a given time window, mitigating denial-of-service risks.

For this specific code, the minimal, non-breaking fix is to (1) import express-rate-limit, (2) define a rate limiter instance suitable for the /assets listing endpoint, and (3) insert that limiter middleware into the /assets route before server.handleRequest(this.getAssets). To avoid altering other routes’ behavior, we will only apply the limiter to the /assets route, not globally. Concretely: at the top of src/Asset/Asset.router.ts we add an import for express-rate-limit. Inside mount(), before defining the routes, we create a const assetsRateLimiter = rateLimit({ ... }) with reasonable defaults, e.g., limiting requests per IP for that route. Then, in the .get('/assets', ...) handler chain, we add assetsRateLimiter between withAuthentication and server.handleRequest(this.getAssets). No existing functions need to change their signatures or logic; we just enrich the middleware chain.

Suggested changeset 2
src/Asset/Asset.router.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Asset/Asset.router.ts b/src/Asset/Asset.router.ts
--- a/src/Asset/Asset.router.ts
+++ b/src/Asset/Asset.router.ts
@@ -13,6 +13,7 @@
 import { getDefaultEthAddress } from '../AssetPack/utils'
 import { Asset } from './Asset.model'
 import { withAuthentication, AuthRequest } from '../middleware/authentication'
+import rateLimit from 'express-rate-limit'
 
 const DEFAULT_ETH_ADDRESS = getDefaultEthAddress()
 
@@ -24,6 +25,10 @@
       AssetPack,
       'assetPackId'
     )
+    const assetsRateLimiter = rateLimit({
+      windowMs: 15 * 60 * 1000, // 15 minutes
+      max: 100, // limit each IP to 100 requests per windowMs for /assets
+    })
 
     /**
      * CORS for the OPTIONS header
@@ -69,6 +74,7 @@
       '/assets',
       withCors,
       withAuthentication,
+      assetsRateLimiter,
       server.handleRequest(this.getAssets)
     )
   }
EOF
@@ -13,6 +13,7 @@
import { getDefaultEthAddress } from '../AssetPack/utils'
import { Asset } from './Asset.model'
import { withAuthentication, AuthRequest } from '../middleware/authentication'
import rateLimit from 'express-rate-limit'

const DEFAULT_ETH_ADDRESS = getDefaultEthAddress()

@@ -24,6 +25,10 @@
AssetPack,
'assetPackId'
)
const assetsRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs for /assets
})

/**
* CORS for the OPTIONS header
@@ -69,6 +74,7 @@
'/assets',
withCors,
withAuthentication,
assetsRateLimiter,
server.handleRequest(this.getAssets)
)
}
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -73,7 +73,8 @@
     "prom-client": "^13.1.0",
     "pull-stream": "^3.6.12",
     "tslint": "^6.1.3",
-    "uuid": "^8.3.2"
+    "uuid": "^8.3.2",
+    "express-rate-limit": "^8.3.1"
   },
   "devDependencies": {
     "@swc/core": "^1.2.102",
EOF
@@ -73,7 +73,8 @@
"prom-client": "^13.1.0",
"pull-stream": "^3.6.12",
"tslint": "^6.1.3",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"express-rate-limit": "^8.3.1"
},
"devDependencies": {
"@swc/core": "^1.2.102",
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 8.3.1 None
Copilot is powered by AI and may make mistakes. Always verify output.
@coveralls
Copy link

Pull Request Test Coverage Report for Build 22962474366

Details

  • 11 of 16 (68.75%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.4%) to 71.907%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/Asset/Asset.model.ts 0 5 0.0%
Totals Coverage Status
Change from base Build 22682665195: 0.4%
Covered Lines: 3243
Relevant Lines: 4321

💛 - Coveralls

await Promise.all(assets.map((asset) => new Asset(asset).upsert()))
await Promise.all(
assets.map((asset) =>
new Asset({ ...asset, asset_pack_id: id }).upsert()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if, instead of rewriting the assets with the asset pack id from the request, we return a 400 if one of them does not have the correct id?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea!

INNER JOIN ${SQL.raw(AssetPack.tableName)} ap ON a.asset_pack_id = ap.id
WHERE a.id = ANY(${ids})
AND ap.eth_address = ANY(${ethAddresses})
AND ap.is_deleted = FALSE`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the is_deleted condition here and not in the original query?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, will add it to the original query, as we don't want to show deleted assets even using the id.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants