a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
113 lines
3.4 KiB
Go
113 lines
3.4 KiB
Go
package contracts
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
"github.com/zzet/gortex/internal/parser"
|
|
)
|
|
|
|
// Play Framework conf/routes extraction. A Play app declares its HTTP routes
|
|
// in an extensionless `conf/routes` file (and per-module `*.routes` includes),
|
|
// one route per line:
|
|
//
|
|
// GET /users controllers.UserController.list()
|
|
// POST /users/:id controllers.UserController.update(id: Long)
|
|
//
|
|
// This pass parses each verb line into a route Contract and stamps the
|
|
// controller class + method so the module-wide cross-file pass binds it to the
|
|
// Scala/Java handler. `#` comments, `->` sub-router includes, and `+modifier`
|
|
// lines emit no routes (matching Play's own routes grammar).
|
|
|
|
// playRouteVerbs is the set of HTTP verbs a Play route line may start with.
|
|
var playRouteVerbs = map[string]bool{
|
|
"GET": true, "POST": true, "PUT": true, "DELETE": true,
|
|
"PATCH": true, "HEAD": true, "OPTIONS": true,
|
|
}
|
|
|
|
// extractPlayRoutes parses a Play conf/routes file into route Contracts.
|
|
func (h *HTTPExtractor) extractPlayRoutes(filePath, text string, lines []string, fileNodes []*graph.Node, lang string, tree *parser.ParseTree) []Contract {
|
|
var out []Contract
|
|
for i, raw := range lines {
|
|
line := strings.TrimSpace(raw)
|
|
// Comments, sub-router includes, and route modifiers are not routes.
|
|
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "->") || strings.HasPrefix(line, "+") {
|
|
continue
|
|
}
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 3 {
|
|
continue
|
|
}
|
|
verb := strings.ToUpper(fields[0])
|
|
if !playRouteVerbs[verb] {
|
|
continue
|
|
}
|
|
path := fields[1]
|
|
if !strings.HasPrefix(path, "/") {
|
|
continue
|
|
}
|
|
fqcn, hclass, hident := playHandlerRef(fields[2])
|
|
if hident == "" {
|
|
continue
|
|
}
|
|
normPath, origNames := NormalizeHTTPPathWithParams(path)
|
|
c := Contract{
|
|
ID: fmt.Sprintf("http::%s::%s", verb, normPath),
|
|
Type: ContractHTTP,
|
|
Role: RoleProvider,
|
|
FilePath: filePath,
|
|
Line: i + 1,
|
|
Confidence: 0.85,
|
|
Meta: map[string]any{
|
|
"method": verb,
|
|
"path": normPath,
|
|
"framework": "play",
|
|
},
|
|
}
|
|
if len(origNames) > 0 {
|
|
c.Meta["path_param_names"] = origNames
|
|
}
|
|
if fqcn != "" {
|
|
c.Meta["handler_fqcn"] = fqcn
|
|
}
|
|
if hclass != "" {
|
|
c.Meta["handler_class"] = hclass
|
|
}
|
|
c.Meta["handler_ident"] = hident
|
|
if id := findMethodByNameAndReceiver(fileNodes, hident, hclass); id != "" {
|
|
c.SymbolID = id
|
|
}
|
|
EnrichHTTPContractWithTree(&c, lines, fileNodes, lang, tree)
|
|
out = append(out, c)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// playHandlerRef parses a Play route handler reference
|
|
// `controllers.UserController.list(id: Long)` into its qualified class
|
|
// (`controllers.UserController`), simple class (`UserController`), and method
|
|
// (`list`). A leading `@` (dependency-injected routing) is tolerated. Returns
|
|
// empty strings when the reference has no `Class.method` shape.
|
|
func playHandlerRef(ref string) (fqcn, hclass, hident string) {
|
|
ref = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(ref), "@"))
|
|
if i := strings.IndexByte(ref, '('); i >= 0 {
|
|
ref = ref[:i]
|
|
}
|
|
ref = strings.TrimSpace(ref)
|
|
i := strings.LastIndex(ref, ".")
|
|
if i < 0 {
|
|
return "", "", ""
|
|
}
|
|
method := ref[i+1:]
|
|
cls := ref[:i]
|
|
if method == "" || cls == "" {
|
|
return "", "", ""
|
|
}
|
|
simple := cls
|
|
if j := strings.LastIndex(cls, "."); j >= 0 {
|
|
simple = cls[j+1:]
|
|
}
|
|
return cls, simple, method
|
|
}
|