@samitouri / QOSami-HFS / commits / a1c6cc0a

fix: possible CSRF attack via GET APIs CVE-2026-61502

Massimo Melina committed Jul 11, 2026 at 11:38 UTC a1c6cc0a0568e03619573e0fc079c07a4a1faef6
2 files changed +6 -3
src/apiMiddleware.ts
+2 -3
@@ -28,12 +28,11 @@ export function apiMiddleware(apis: ApiHandlers) : Koa.Middleware {
28 const params = isPost ? ctx.state.params || {} : ctx.query
29 const apiName = ctx.path
30 console.debug('API', ctx.method, apiName, { ...params })
31 - const csrfSafe = !isPost
32 - || ctx.get('x-hfs-anti-csrf') // automatic browser actions won't carry this header
31 + const csrfSafe = ctx.get('x-hfs-anti-csrf') // automatic browser actions won't carry this header
32 || apiName.startsWith('get_') // "get_" apis are safe because they make no change
33 || /^(curl|wget|python|go-|java|axios|postman|httpie|insomnia|bruno)/i.test(ctx.get('user-agent') || '') // only browser are subject to CSRF
34 if (!csrfSafe)
36 - return send(HTTP_FOOL, "missing header x-hfs-anti-csrf=1")
35 + return send(HTTP_FOOL, "missing header x-hfs-anti-csrf:1")
36 const customApiRest = apiName.startsWith(PLUGIN_CUSTOM_REST_PREFIX) && apiName.slice(PLUGIN_CUSTOM_REST_PREFIX.length)
37 const apiFun = customApiRest && firstPlugin(pl => pl.getData().customRest?.[customApiRest])
38 || apis.hasOwnProperty(apiName) && apis[apiName]!
tests/test.ts
+4
@@ -177,6 +177,10 @@ describe('basics', () => {
177 test('missing perm', reqList('/for-admins/', 401))
178 test('missing perm.file', req('/for-admins/alfa.txt', 401))
179 test('missing anti-csrf', reqApi('rename', { uri: '/f1', dest: 'x' }, 418, { headers: {} })) // overriding anti-csrf
180 + test('missing anti-csrf.get mutation', req(API + 'add_account?username=csrf&password=x&admin=true', 418, {
181 + headers: { 'user-agent': 'Mozilla/5.0' },
182 + jar: {},
183 + }))
184 test('malformed body', reqApi('rename', { uri: '/f1', dest: 'x' }, { status: 400 }, {
185 headers: { 'x-hfs-anti-csrf': '1', 'content-type': 'application/json' },
186 body: '{'