VNX-425: Direct Request ('Forced Browsing')
Reports an administrative route (a path segment admin, administration, backoffice, superuser, sysadmin, staff or manage/management) registered with no authentication in front of it: Express/Koa/Fastify routes with no middleware, and Go handlers or gin/echo admin route groups with no middleware or in-handler check. The same defect on a Python route is VNX-306.
| Field | Value |
|---|---|
| Rule ID | VNX-425 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript, Go |
| CWE | CWE-425 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | direct-request forced-browsing missing-authorization cwe-425 |
| Source | internal/sast/rules/vnx-425.rego |
Overview
VNX-425 maps to CWE-425: Direct Request (‘Forced Browsing’). An administrative page that is merely unlinked is not protected: anyone who guesses or reads its URL can request it directly. This rule reports admin routes that are registered without any authentication or authorisation check.
Reported:
- JavaScript and TypeScript (Express-style):
app/router.get/.post/.put/.patch/.delete/.allwhose only argument after the path is the handler (the path may be on the line after an open call), unless an inline handler checksreq.user/req.sessionor compares a role (role === ...,roles.includes(...),hasRole), only redirects, or the file mounts authentication middleware with.use()globally or on a prefix of the path. - Go:
HandleFunc/Handleand gin/echo/chiGET/POST/… with a bare handler name, in a file with no.Use(middleware, when the handler is defined in the same file and does not check authentication in its first 15 lines.
What Gets Flagged
app.get('/admin/users', adminController.listUsers) // FLAGGED
manageRouter.delete( // FLAGGED: path on the next line
'/administrator/cache', (req, res) => { purgeCache(); res.end(); });
http.HandleFunc("/admin/dashboard", adminDashboard) // FLAGGED
What Is Not Flagged
- Routes whose path is not administrative.
- Routes with middleware (
app.get('/admin', isLoggedIn, isAdmin, h)) or a check on the user inside the handler. Writing a role ({ role: 'admin' }) is not a check. - Files that mount auth middleware globally (
router.use(passport.authenticate('jwt'))) or on the route’s prefix. - Go handlers wrapped in a call (
requireAdmin(h)) or defined in another file. - NestJS controllers and Spring endpoints, whose guards usually live elsewhere.
- Route calls inside string literals, test files and comments.
Reported by other rules, so one route is one finding:
- An administrative Flask, FastAPI or Quart route with nothing in front of it is reported by VNX-306.
- A Spring or JAX-RS handler that
@PermitAllopens to everyone is reported by VNX-935.
Remediation
Put every administrative route behind a check that the caller is an authenticated administrator:
app.get('/admin/users', isLoggedIn, isAdmin, adminController.listUsers)
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-425
// nosec vnx-425
Or record an ignore rule: vulnetix ignore add --rule vnx-425 --reason "...". See Suppressing findings.