Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions libs/openant-core/parsers/go/go_parser/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,13 @@ var httpHandlerPatterns = []*regexp.Regexp{

func (e *Extractor) isHTTPHandler(params, returns []string, code string) bool {
paramsStr := strings.Join(params, " ")
returnsStr := strings.Join(returns, " ")

// Check parameters for HTTP patterns
// Check parameters for HTTP patterns. A handler is identified by what it
// RECEIVES (request/context types in params), not by what it returns —
// matching these patterns against the return type mis-tagged factories
// such as `func Logger() gin.HandlerFunc` as http_handler entry points (F10).
for _, pattern := range httpHandlerPatterns {
if pattern.MatchString(paramsStr) || pattern.MatchString(returnsStr) {
if pattern.MatchString(paramsStr) {
return true
}
}
Expand Down Expand Up @@ -325,11 +327,20 @@ func (e *Extractor) isCLIHandler(params, returns []string, code string) bool {
func (e *Extractor) isMiddleware(params, returns []string, code string) bool {
// Middleware often takes and returns http.Handler or similar
returnsStr := strings.Join(returns, " ")
paramsStr := strings.Join(params, " ")

// A bare `next(` substring previously matched any body with a next() call
// (Go 1.23 iterators, lexers, cursors), mislabelling them middleware (F7).
// Only treat a `next(` call as middleware when the function has an HTTP
// request signature, i.e. it is actually wrapping a handler.
hasHTTPSignature := strings.Contains(paramsStr, "http.ResponseWriter") ||
strings.Contains(paramsStr, "http.Request") ||
strings.Contains(paramsStr, "http.Handler")

if strings.Contains(returnsStr, "http.Handler") ||
strings.Contains(returnsStr, "http.HandlerFunc") ||
strings.Contains(code, "next.ServeHTTP") ||
strings.Contains(code, "next(") {
(hasHTTPSignature && strings.Contains(code, "next(")) {
return true
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import "testing"

// Regression tests for F7 and F10 — over-broad unit-type classification in
// classifyUnitType, which seeds false remote-web entry points downstream.

// F10: a factory that *returns* http.HandlerFunc (no request-shaped params,
// no handler body) must NOT be classified http_handler — only a function that
// RECEIVES a request is a handler.
func TestFactoryReturningHandlerFuncIsNotHTTPHandler(t *testing.T) {
e := NewExtractor(".")
got := e.classifyUnitType(
"NewLoggingHandler", "",
[]string{"prefix string"}, // params: no request types
[]string{"http.HandlerFunc"}, // returns a handler (factory)
"{ return func(w http.ResponseWriter, r *http.Request) {} }",
"x.go",
)
if got == UnitTypeHTTPHandler {
t.Fatalf("factory returning http.HandlerFunc classified as %q, want != http_handler", got)
}
}

// F7: an iterator/lexer whose body merely contains a "next(" call must NOT be
// classified middleware. Real middleware returns http.Handler/HandlerFunc or
// calls next.ServeHTTP / next(w, r) with an http signature.
func TestIteratorWithNextCallIsNotMiddleware(t *testing.T) {
e := NewExtractor(".")
got := e.classifyUnitType(
"CutPrefix", "",
[]string{"s string", "prefix string"}, // no http params
[]string{"string", "bool"}, // no http returns
"{ for next, hasNext := iter(); hasNext; { _ = next(); } ; return s, true }",
"seq.go",
)
if got == UnitTypeMiddleware {
t.Fatalf("iterator using next() classified as %q, want != middleware", got)
}
}

// Guard against over-correction: a genuine net/http handler and genuine
// middleware must still be detected.
func TestGenuineHTTPHandlerStillDetected(t *testing.T) {
e := NewExtractor(".")
got := e.classifyUnitType(
"ServeIndex", "",
[]string{"w http.ResponseWriter", "r *http.Request"},
[]string{},
"{ w.Write([]byte(\"ok\")) }",
"h.go",
)
if got != UnitTypeHTTPHandler {
t.Fatalf("genuine handler classified as %q, want http_handler", got)
}
}

func TestGenuineMiddlewareStillDetected(t *testing.T) {
e := NewExtractor(".")
got := e.classifyUnitType(
"Logging", "",
[]string{"next http.Handler"},
[]string{"http.Handler"},
"{ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ next.ServeHTTP(w, r) }) }",
"mw.go",
)
if got != UnitTypeMiddleware {
t.Fatalf("genuine middleware classified as %q, want middleware", got)
}
}
Loading