Vue Router Navigation Security Review
Purpose
Review Vue Router navigation guards, redirect flows, dynamic link bindings, and route
configuration for the security-critical defect classes specific to client-side routing as a
security surface — not general route-tree architecture, not component design, and not the
state-store or SSR-hydration concerns owned by sibling skills. This skill exists to keep the
review anchored to five documented defect classes: (a) a beforeEach/beforeEnter guard used
as the sole authorization boundary with no server-side enforcement behind it, (b) open redirect
via route.query.redirect/returnUrl passed unvalidated into router.push()/
window.location, (c) javascript:/data: scheme injection via a dynamic :to/:href
binding, (d) route.params/route.query interpolated into v-html/innerHTML (reflected XSS
through the router), and (e) next(unvalidatedPath)/guard-returned redirects creating loops or
bypasses, overly-broad catch-all routes, and history-mode server misconfiguration. It does not
re-litigate general route-tree/code-splitting architecture, Pinia/Vuex store security, or SSR
cross-request pollution in every response.
When to use
Use this skill when the user asks to:
- review a
router.beforeEach/beforeEnterguard (or a NuxtdefinePageMeta/route-middleware equivalent) that gates access to a route, - audit a login/logout flow's post-authentication redirect handling,
- assess whether a
<router-link :to="...">or dynamic:hrefbinding is safe from scheme injection, - investigate whether
route.params/route.queryreaching av-htmlor.innerHTMLsink is exploitable, - review a route table for redirect-loop risk, catch-all overreach, or
history-mode server-fallback exposure, - perform a pre-launch security review of a Vue Router-based application's navigation layer.
Do not use this skill for:
- general route-tree structure, loader/code-splitting, or focus-management review with no
security angle — use
routing-navigation-reviewinstead (and note it targets React Router/Next.js, not Vue Router), - Pinia/Vuex store security (persisted sensitive data, SSR store-singleton pollution, untrusted
hydration payloads, client-held role flags as an authorization source) — use
vue-state-store-security-reviewinstead, - SSR entry-point cross-request state pollution or general
v-html/dynamic-URL injection unrelated to router data — usevue-ssr-security-reviewinstead; this skill's injection scope is specificallyroute.params/route.queryreaching a sink, not everyv-htmlin the app, - broad token-storage, cookie-flag, CSRF, or OAuth/OIDC flow review with no router-specific
mechanism in play — use
frontend-auth-session-security-reviewinstead; this skill covers only the router-mediated open-redirect mechanism (route.query.redirect/returnUrl), not session/token architecture generally, - confirming a bypass has already been exploited in production (concurrent-request reproduction, live penetration testing, a captured attack) — static analysis proves the structural risk, not that it has already been exploited.
Context7 Documentation Protocol
- Resolve the Vue Router library ID with
resolve-library-id(matched result:/vuejs/router, Vue Router's own source-and-docs repository) before citing any guard-mechanism, redirect-API, or dynamic-matching claim. - Use
query-docsagainst/vuejs/routerto corroborate: navigation-guard return semantics (beforeEach/beforeEnterreturningfalse/a location/undefined, and the legacynextargument's call-exactly-once contract), the documentedto.name !== 'Login'self-exclusion pattern, redirect function shapes (redirect: to => ({ path, query })),route.params/route.queryexposure with no built-in escaping,router.push's acceptance of bare string paths,createWebHistory()'s server-fallback requirement, and the/:pathMatch(.*)*catch-all pattern. These are allrepo evidenceclaims — cite the specific guide file (navigation-guards, redirect-and-alias, dynamic-matching, history-mode) alongside the claim. - The conclusion that a client-side guard must not be the sole authorization boundary is not
itself a Vue-Router API fact — Vue Router's docs describe guards strictly as navigation
control flow (cancel/redirect/proceed), never as an access-control mechanism. Label this
specific conclusion
inference, grounded in general server-side-enforcement security practice (documentation-basedvia OWASP), layered on therepo evidencefact that guards are browser-executed callbacks. - The claim that
v-htmlbypasses Vue's default template auto-escaping is general Vue template-compiler behavior, not a Vue-Router API — ground it in the Vue security guide (documentation-based, listed in this skill'sofficial_docs), not in/vuejs/router. - Read
package.jsonfirst to confirm the Vue Router major version in use — guard signatures (return-value guards vs. the legacynextthird argument), redirect object shape, and catch-all syntax (/:pathMatch(.*)*in v4 vs. legacy*in v3) differ across majors; do not apply v4 API names to a v3 codebase or vice versa. - If Context7 is unavailable, fall back to the
official_docsURLs in this skill'smetadata.jsonand label the claimdocumentation-based, unverified against current release.
Lean operating rules
- Every finding in this skill's scope defaults to HIGH severity: broken client-side-only access control, open redirect, scheme injection, and reflected XSS are all directly exploitable without further chaining. Do not downgrade to MEDIUM because "it hasn't been reported exploited yet" — the risk is structural.
- Trace every finding to a concrete file:line and a concrete data-flow path (guard → endpoint gap, or origin → sink hops for injection/redirect findings). A finding that says "this guard might not be enough" or "this redirect might be exploitable" without the specific trace is a guess, not a finding.
- Never flag "a
beforeEach/beforeEnterguard exists" by itself. The finding requires the absence of confirmed server-side enforcement on the endpoint the protected route depends on — look for evidence (a 401/403 check, a BFF layer, an API contract note) before concluding it's missing, and state explicitly what you found or didn't find. - Never accept a type check or substring blocklist as clearing an open-redirect or
scheme-injection finding. The only acceptable control is an allowlist: same-origin/
relative-path validation for redirects, and an explicit protocol allowlist
(
http:/https:/mailto:) for dynamic link bindings. - Do not approve a
v-html/.innerHTMLsink fed byroute.params/route.queryunless a named sanitizer call is visibly present on that exact traced path — a sanitizer existing elsewhere in the codebase does not clear this bar. - Check every unconditional redirect-on-guard for the framework's own documented self-exclusion
pattern (
to.name !== 'Login'or equivalent). Its absence is a HIGH redirect-loop finding, not a style nit — Vue Router's docs treat it as required. - Read the full
routesarray in registration order before clearing a catch-all route; a broad/catch-all pattern registered before a route that should require auth can shadow it. - Never execute, build, or run application code, and never send live requests, as part of this review; this is a static-review skill (Read/Grep/Glob only).
- Load only the reference needed for the concern in scope.
References
Load these only when needed:
- Review workflow and findings contract — use for the step-by-step review procedure, the full decision tree across all five defect classes, and the required output shape.
- Client-side guards as an authorization boundary, and navigation control-flow risks
— load when reviewing a
beforeEach/beforeEnterguard, a guard-returned redirect, a catch-all route, orhistory-mode configuration. - Open redirect, scheme injection, and reflected XSS through the router
— load when reviewing a post-login redirect flow, a dynamic
:to/:hrefbinding, or av-html/innerHTMLsink fed byroute.params/route.query.
Response minimum
Return, at minimum:
- the guard(s), redirect flow(s), dynamic link binding(s), route table, and/or
v-html/innerHTMLsink(s) in scope, - ranked findings with file:line evidence, defect category
(
client-guard-as-sole-authz/open-redirect/scheme-injection/reflected-xss/redirect-loop/catch-all-misconfig/history-mode-server-misconfig), the concrete data-flow trace (the guard-to-endpoint gap, or the full origin-to-sink path naming every hop), and a fix sketch matching Vue Router's documented pattern, - for every guard finding, an explicit statement of whether server-side enforcement evidence was found and where — never infer it exists without evidence on the traced path,
- evidence level per finding (
repo evidence,documentation-based, orinference), with structural-risk findings explicitly labeled as structural risk, not confirmed-exploited, - verdict (approve / approve-with-notes / block),
- open questions or scope the review could not cover (e.g., "confirming the API layer actually rejects unauthorized requests requires a live request or reading server-side code outside this diff's scope").