fix(auth): закрыть обход JWT через закодированные URL

This commit is contained in:
Denozordec committed 2026-10-13 21:23:51 +07:00
1 parent 727e35f985
commit 7d86af7e8b
19 files changed
+1656 -23

No files matched your search

@@ -0,0 +1,30 @@
{
"date": "2026-10-13",
"block": "12.5.211",
"runtime": "ux-gre209",
"cases": [
{
"path": "/api/servers",
"status": 401,
"anonymous": true,
"serverRows": 0,
"nonemptyPasswordPresent": false
},
{
"path": "/api/%73ervers",
"status": 401,
"anonymous": true,
"serverRows": 0,
"nonemptyPasswordPresent": false
},
{
"path": "/%61pi/servers",
"status": 200,
"anonymous": true,
"serverRows": 2,
"nonemptyPasswordPresent": true
}
],
"rawBodyStored": false,
"mutationRequests": 0
}
@@ -0,0 +1,24 @@
{
"date": "2026-10-13",
"block": "12.5.211",
"services": {
"backend": {
"image": "mmapp-test/backend:ux-auth211",
"imageId": "sha256:904c844a729465e8cdb48278a1f2829c88fe2764941ae60b4a0d05dc2f2d3b18",
"running": true,
"health": "healthy"
},
"frontend": {
"image": "mmapp-test/frontend:ux-gre210",
"imageId": "sha256:b4b1280f729e89de83b30bd242d37bda831306720b3c78cb9eef6048c3e3178e",
"running": true,
"health": null
}
},
"rollbackTags": {
"backend": "ux-gre210",
"frontend": "ux-gre210"
},
"backendSourceMatched": 477,
"compiledMatched": 1048
}
@@ -0,0 +1,193 @@
{
"date": "2026-10-13",
"block": "12.5.211",
"backend": "ux-auth211",
"frontend": "ux-gre210",
"cases": [
{
"role": "anonymous",
"path": "/api/servers",
"method": "GET",
"status": 401
},
{
"role": "anonymous",
"path": "/api/servers",
"method": "HEAD",
"status": 401
},
{
"role": "anonymous",
"path": "/api/%73ervers",
"method": "GET",
"status": 401
},
{
"role": "anonymous",
"path": "/api/%73ervers",
"method": "HEAD",
"status": 401
},
{
"role": "anonymous",
"path": "/%61pi/servers",
"method": "GET",
"status": 401
},
{
"role": "anonymous",
"path": "/%61pi/servers",
"method": "HEAD",
"status": 401
},
{
"role": "anonymous",
"path": "/%61%70%69/servers",
"method": "GET",
"status": 401
},
{
"role": "anonymous",
"path": "/%61%70%69/servers",
"method": "HEAD",
"status": 401
},
{
"role": "dashboard",
"path": "/api/servers",
"method": "GET",
"status": 403,
"privateMaterialAbsent": true
},
{
"role": "dashboard",
"path": "/api/%73ervers",
"method": "GET",
"status": 403,
"privateMaterialAbsent": true
},
{
"role": "dashboard",
"path": "/%61pi/servers",
"method": "GET",
"status": 403,
"privateMaterialAbsent": true
},
{
"role": "dashboard",
"path": "/api/gre/tunnels",
"method": "GET",
"status": 403,
"pskAbsent": true
},
{
"role": "dashboard",
"path": "/api/gre/tunnels",
"method": "PATCH",
"status": 403,
"emptyBody": true,
"nativeApplication": false
},
{
"role": "dashboard",
"path": "/api/%67re/tunnels",
"method": "GET",
"status": 403,
"pskAbsent": true
},
{
"role": "dashboard",
"path": "/api/%67re/tunnels",
"method": "PATCH",
"status": 403,
"emptyBody": true,
"nativeApplication": false
},
{
"role": "dashboard",
"path": "/%61pi/gre/tunnels",
"method": "GET",
"status": 403,
"pskAbsent": true
},
{
"role": "dashboard",
"path": "/%61pi/gre/tunnels",
"method": "PATCH",
"status": 403,
"emptyBody": true,
"nativeApplication": false
},
{
"role": "reader",
"path": "/api/servers",
"method": "GET",
"status": 200,
"privateMaterialAbsent": true
},
{
"role": "reader",
"path": "/api/%73ervers",
"method": "GET",
"status": 200,
"privateMaterialAbsent": true
},
{
"role": "reader",
"path": "/%61pi/servers",
"method": "GET",
"status": 200,
"privateMaterialAbsent": true
},
{
"role": "reader",
"path": "/api/gre/tunnels",
"method": "GET",
"status": 200,
"pskAbsent": true
},
{
"role": "reader",
"path": "/api/gre/tunnels",
"method": "PATCH",
"status": 403,
"emptyBody": true,
"nativeApplication": false
},
{
"role": "reader",
"path": "/api/%67re/tunnels",
"method": "GET",
"status": 200,
"pskAbsent": true
},
{
"role": "reader",
"path": "/api/%67re/tunnels",
"method": "PATCH",
"status": 403,
"emptyBody": true,
"nativeApplication": false
},
{
"role": "reader",
"path": "/%61pi/gre/tunnels",
"method": "GET",
"status": 200,
"pskAbsent": true
},
{
"role": "reader",
"path": "/%61pi/gre/tunnels",
"method": "PATCH",
"status": 403,
"emptyBody": true,
"nativeApplication": false
}
],
"publicAuthConfig": 200,
"realPortalJWT": true,
"rawResponseBodiesStored": false,
"ownedUsersRemoved": true,
"scope": "Anonymous GET/HEAD and actual restricted roles; denied PATCH uses empty body and cannot apply native objects"
}
@@ -0,0 +1,195 @@
{
"date": "2026-10-13",
"block": "12.5.211",
"database": "mm_accept_166_1791901195054",
"baseline": false,
"cases": [
{
"path": "/api/servers",
"method": "GET",
"role": "anonymous",
"status": 401
},
{
"path": "/api/servers",
"method": "HEAD",
"role": "anonymous",
"status": 401
},
{
"path": "/api/%73ervers",
"method": "GET",
"role": "anonymous",
"status": 401
},
{
"path": "/api/%73ervers",
"method": "HEAD",
"role": "anonymous",
"status": 401
},
{
"path": "/%61pi/servers",
"method": "GET",
"role": "anonymous",
"status": 401
},
{
"path": "/%61pi/servers",
"method": "HEAD",
"role": "anonymous",
"status": 401
},
{
"path": "/%61%70%69/servers",
"method": "GET",
"role": "anonymous",
"status": 401
},
{
"path": "/%61%70%69/servers",
"method": "HEAD",
"role": "anonymous",
"status": 401
},
{
"path": "/api/gre/tunnels",
"method": "GET",
"role": "anonymous",
"status": 401
},
{
"path": "/api/gre/tunnels",
"method": "HEAD",
"role": "anonymous",
"status": 401
},
{
"path": "/%61pi/gre/tunnels",
"method": "GET",
"role": "anonymous",
"status": 401
},
{
"path": "/%61pi/gre/tunnels",
"method": "HEAD",
"role": "anonymous",
"status": 401
},
{
"path": "/api/gre/tunnels",
"method": "GET",
"role": "dashboard",
"status": 403,
"privateMaterialStored": false
},
{
"path": "/api/gre/tunnels",
"method": "PATCH",
"role": "dashboard",
"status": 403
},
{
"path": "/api/gre/tunnels",
"method": "GET",
"role": "reader",
"status": 200,
"privateMaterialStored": false
},
{
"path": "/api/gre/tunnels",
"method": "PATCH",
"role": "reader",
"status": 403
},
{
"path": "/api/gre/tunnels",
"method": "GET",
"role": "writer",
"status": 200,
"privateMaterialStored": false
},
{
"path": "/api/%67re/tunnels",
"method": "GET",
"role": "dashboard",
"status": 403,
"privateMaterialStored": false
},
{
"path": "/api/%67re/tunnels",
"method": "PATCH",
"role": "dashboard",
"status": 403
},
{
"path": "/api/%67re/tunnels",
"method": "GET",
"role": "reader",
"status": 200,
"privateMaterialStored": false
},
{
"path": "/api/%67re/tunnels",
"method": "PATCH",
"role": "reader",
"status": 403
},
{
"path": "/api/%67re/tunnels",
"method": "GET",
"role": "writer",
"status": 200,
"privateMaterialStored": false
},
{
"path": "/%61pi/gre/tunnels",
"method": "GET",
"role": "dashboard",
"status": 403,
"privateMaterialStored": false
},
{
"path": "/%61pi/gre/tunnels",
"method": "PATCH",
"role": "dashboard",
"status": 403
},
{
"path": "/%61pi/gre/tunnels",
"method": "GET",
"role": "reader",
"status": 200,
"privateMaterialStored": false
},
{
"path": "/%61pi/gre/tunnels",
"method": "PATCH",
"role": "reader",
"status": 403
},
{
"path": "/%61pi/gre/tunnels",
"method": "GET",
"role": "writer",
"status": 200,
"privateMaterialStored": false
},
{
"path": "/api/auth/config",
"role": "anonymous",
"status": 200,
"publicConfig": true
},
{
"path": "/%61pi/auth/config",
"role": "anonymous",
"status": 200,
"publicConfig": true
}
],
"scope": "Production compiled canonical auth/RBAC using own restricted PostgreSQL, local native HTTP fixture and synthetic JWT/PSK",
"passed": true,
"nativeWrites": 0,
"databaseAndRoleRemoved": true
}
@@ -0,0 +1,13 @@
{
"date": "2026-10-13",
"block": "12.5.211",
"canonicalAuthCases": 42,
"commands": [
"npm --prefix backend run test:auth",
"npx tsx backend/src/plugins/auth.geoip.test.ts",
"npx tsx backend/src/plugins/auth.netflow-settings.test.ts"
],
"passed": true,
"backendBuildPassed": true,
"scope": "Matched static/parameter routes, GET/HEAD/PATCH, OSPF read preview, public auth config/health, existing auth/GeoIP/private NetFlow export policy"
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,10 @@
{
"date": "2026-10-13",
"block": "12.5.211",
"kinds": [
{
"kind": "backend",
"matched": 477
}
]
}