diff --git a/libs/openant-core/parsers/go/go_parser/extractor.go b/libs/openant-core/parsers/go/go_parser/extractor.go index 5f3a1d13..c83fa5ab 100644 --- a/libs/openant-core/parsers/go/go_parser/extractor.go +++ b/libs/openant-core/parsers/go/go_parser/extractor.go @@ -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 } } @@ -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 } diff --git a/libs/openant-core/parsers/go/go_parser/extractor_classify_test.go b/libs/openant-core/parsers/go/go_parser/extractor_classify_test.go new file mode 100644 index 00000000..1d112e51 --- /dev/null +++ b/libs/openant-core/parsers/go/go_parser/extractor_classify_test.go @@ -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) + } +}