module GoRouteGroupScope
Overview
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, soapi.Use(...)may sit anywhere below, and a siblingadmin := 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.
Direct including types
Defined in:
tagger/framework_taggers/go/group_scope.crConstant Summary
-
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_CALLcannot see the receiver here (the character before.Useis)), 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/.Partyclosure 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 Method Summary
-
.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"
Instance Method Summary
-
#each_group_scoped_line(content : String, &) : Nil
Walk
contentline by line, maintaining the route-group state, and yield each stripped line alongside it. -
#prefix_covers?(prefix : String, url : String) : Bool
Segment-aware prefix match so "/web" guards "/web/x" but not "/website".
-
#resolve_use_scope(stripped : String, scopes : Scopes) : Scope | Nil
The scope a middleware registration on
strippedguards, or nil when the line registers no middleware.
Class Method Detail
Join a base prefix and a new path segment into a normalized URL prefix: ("", "/web") -> "/web" ("/api", "v1") -> "/api/v1"
Instance Method Detail
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.
Segment-aware prefix match so "/web" guards "/web/x" but not "/website". The root scope "/" guards every endpoint.
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.