module

GoRouteGroupScope

Inherits PrefixScope

Shared Go route-group scope resolution for the Go framework taggers.

Both go_auth and go_security answer the same question: a middleware registration (x.Use(...)) appears on some line — which URL prefix does it guard? Go routers express grouping two structurally different ways, and they have to be tracked differently:

  • assignment groups — api := r.Group("/api"). The variable carries the prefix; the group is NOT delimited by braces, so api.Use(...) may sit anywhere below, and a sibling admin := r.Group("/admin") is a separate scope, not a nested one.
  • closure groups — r.Route("/api", func(r chi.Router) { ... }). The prefix applies to everything inside the closure, so it is delimited by brace depth.

Modelling both with a single push/pop stack makes sibling assignment groups accumulate: /api then /admin resolves the second group's middleware to /api/admin. That is both a false negative (the real /admin/* routes lose their tag) and — worse for a security tool — a false positive (/api/admin/* routes are reported as guarded when they are not).

The third case is a group whose path is not a string literal (r.Group(cfg.APIBase + "/v1")). Its prefix is genuinely unknowable to a line-based scanner, and it must NOT collapse to "global": a real app that builds every group that way would have every endpoint — including its explicitly public ones — tagged from whichever .Use(auth) happened to be scanned first. Unknown keeps that case distinguishable from Global so callers can decline to tag, matching spring_security's treatment of a filter chain scoped only by a matcher it cannot resolve.

Constants

ASSIGN_GROUP = /(\w+)\s*:?=\s*(\w+)\.(?:Group|Route|Party|PartyFunc)\s*\(\s*"([^"]*)"/

name := parent.Group("/seg") — assignment group with a literal path.

ASSIGN_GROUP_ANY = /(\w+)\s*:?=\s*(\w+)\.(?:Group|Route|Party|PartyFunc)\s*\(/

The same assignment shape with any first argument, literal or not. Used to tell "this variable is a route group whose prefix we can't read" from "this variable is not a route group at all".

CHAINED_GROUP_USE = /(\w+)\.(?:Group|Route|Party|PartyFunc)\s*\(\s*"([^"]*)"\s*\)\s*\.\s*(?:Use|Pre)\s*\(/

r.Group("/api").Use(auth) — the group is created and the middleware registered in one chained expression, so it is neither an assignment nor a closure group. USE_CALL cannot see the receiver here (the character before .Use is )), so match the chain directly.

CHAINED_GROUP_USE_ANY = /(\w+)\.(?:Group|Route|Party|PartyFunc)\s*\([^)]*\)\s*\.\s*(?:Use|Pre)\s*\(/
CLOSURE_GROUP = /(\w+)\.(?:Group|Route|Party|PartyFunc|Mount)\s*\(\s*"([^"]*)"\s*,\s*func/

parent.Group("/seg", func(...) / .Route / .Party closure group.

CLOSURE_GROUP_ANY = /(\w+)\.(?:Group|Route|Party|PartyFunc|Mount)\s*\(.*\bfunc\b/
GLOBAL_SCOPE = {kind: ScopeKind::Global, prefix: "/"}
UNKNOWN_SCOPE = {kind: ScopeKind::Unknown, prefix: "/"}
USE_CALL = /(\w+)\.(?:Use|Pre)\s*\(/

A .Use(...) / .Pre(...) middleware registration call.

Class methods

join_prefix(base : String, seg : String) : String

Join a base prefix and a new path segment into a normalized URL prefix: ("", "/web") -> "/web" ("/api", "v1") -> "/api/v1"

Source

Instance methods

each_group_scoped_line(content : String, &) : Nil

Walk content line by line, maintaining the route-group state, and yield each stripped line alongside it. Callers resolve a middleware registration with resolve_use_scope.

Source
resolve_use_scope(stripped : String, scopes : Scopes) : Scope | Nil

The scope a middleware registration on stripped guards, or nil when the line registers no middleware. Callers must decline to tag an Unknown scope rather than treating it as global.

Source

Nested types