fix: folders served as web-pages shouldn't be considered downloads #697
Massimo Melina committed
Sep 21, 2024 at 11:18 UTC
255507ba65e72ab2905ea167186aabb37cedcaa1
3 files changed
+14
-9
src/log.ts
+1
@@ -151,6 +151,7 @@ declare module "koa" {
151
logExtra?: object
152
completed?: Promise<unknown>
153
spam?: boolean // this request was marked as spam
154
+ considerAsGui?: boolean
155
}
156
}
157
src/serveFile.ts
+7
-7
@@ -17,9 +17,9 @@ import { sendErrorPage } from './errorPages'
17
import { Readable } from 'stream'
18
19
const allowedReferer = defineConfig('allowed_referer', '')
20
-const limitDownloads = downloadLimiter(defineConfig(CFG.max_downloads, 0), () => true)
21
-const limitDownloadsPerIp = downloadLimiter(defineConfig(CFG.max_downloads_per_ip, 0), ctx => ctx.ip)
22
-const limitDownloadsPerAccount = downloadLimiter(defineConfig(CFG.max_downloads_per_account, 0), ctx => getCurrentUsername(ctx) || undefined)
20
+const maxDownloads = downloadLimiter(defineConfig(CFG.max_downloads, 0), () => true)
21
+const maxDownloadsPerIp = downloadLimiter(defineConfig(CFG.max_downloads_per_ip, 0), ctx => ctx.ip)
22
+const maxDownloadsPerAccount = downloadLimiter(defineConfig(CFG.max_downloads_per_account, 0), ctx => getCurrentUsername(ctx) || undefined)
23
24
export async function serveFileNode(ctx: Koa.Context, node: VfsNode) {
25
const { source, mime } = node
@@ -28,7 +28,7 @@ export async function serveFileNode(ctx: Koa.Context, node: VfsNode) {
28
: _.find(mime, (val,mask) => matches(name, mask))
29
if (allowedReferer.get()) {
30
const ref = /\/\/([^:/]+)/.exec(ctx.get('referer'))?.[1] // extract host from url
31
- if (ref && ref !== host() // automatic accept if referer is basically the hosting domain
31
+ if (ref && ref !== host() // automatically accept if referer is basically the hosting domain
32
&& !matches(ref, allowedReferer.get()))
33
return ctx.status = HTTP_FORBIDDEN
34
}
@@ -41,8 +41,8 @@ export async function serveFileNode(ctx: Koa.Context, node: VfsNode) {
41
ctx.state.considerAsGui = true
42
await serveFile(ctx, source||'', mimeString)
43
44
- if (await limitDownloadsPerAccount(ctx) === undefined) // returning false will not execute other limits
45
- await limitDownloads(ctx) || await limitDownloadsPerIp(ctx)
44
+ if (await maxDownloadsPerAccount(ctx) === undefined) // returning false will not execute other limits
45
+ await maxDownloads(ctx) || await maxDownloadsPerIp(ctx)
46
47
function host() {
48
const s = ctx.get('host')
@@ -150,7 +150,7 @@ declare module "koa" {
150
function downloadLimiter<T>(configMax: { get: () => number | undefined }, cbKey: (ctx: Koa.Context) => T | undefined) {
151
const map = new Map<T, number>()
152
return (ctx: Koa.Context) => {
153
- if (!ctx.body || ctx.state.considerAsGui) return // no file sent, cache hit
153
+ if (!ctx.body || ctx.state.considerAsGui) return // !body = no file sent, cache hit
154
const k = cbKey(ctx)
155
if (k === undefined) return // undefined = skip limit
156
const max = configMax.get()
src/serveGuiAndSharedFiles.ts
+6
-2
@@ -96,8 +96,12 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
96
return
97
}
98
const { get } = ctx.query
99
- if (node.default && path.endsWith('/') && !get) // final/ needed on browser to make resource urls correctly with html pages
100
- node = await urlToNode(node.default, ctx, node) ?? node
99
+ if (node.default && path.endsWith('/') && !get) { // final/ needed on browser to make resource urls correctly with html pages
100
+ const found = await urlToNode(node.default, ctx, node)
101
+ if (found && /\.html?/i.test(node.default))
102
+ ctx.state.considerAsGui = true
103
+ node = found ?? node
104
+ }
105
if (get === 'icon')
106
return serveFile(ctx, node.icon || '|') // pipe to cause not-found
107
if (!await nodeIsDirectory(node))