- Implemented a new tool `stella-callgraph-node` that extracts call graphs from JavaScript/TypeScript projects using Babel AST. - Added command-line interface with options for JSON output and help. - Included functionality to analyze project structure, detect functions, and build call graphs. - Created a package.json file for dependency management. feat: introduce stella-callgraph-python for Python call graph extraction - Developed `stella-callgraph-python` to extract call graphs from Python projects using AST analysis. - Implemented command-line interface with options for JSON output and verbose logging. - Added framework detection to identify popular web frameworks and their entry points. - Created an AST analyzer to traverse Python code and extract function definitions and calls. - Included requirements.txt for project dependencies. chore: add framework detection for Python projects - Implemented framework detection logic to identify frameworks like Flask, FastAPI, Django, and others based on project files and import patterns. - Enhanced the AST analyzer to recognize entry points based on decorators and function definitions.
144 lines
3.6 KiB
Go
144 lines
3.6 KiB
Go
// Framework detection for Go projects
|
|
package main
|
|
|
|
import (
|
|
"golang.org/x/tools/go/ssa"
|
|
"strings"
|
|
)
|
|
|
|
// FrameworkPattern defines detection patterns for a framework
|
|
type FrameworkPattern struct {
|
|
Name string
|
|
Packages []string
|
|
EntrypointFns []string
|
|
HandlerType string
|
|
}
|
|
|
|
// Known Go web frameworks
|
|
var frameworkPatterns = []FrameworkPattern{
|
|
{
|
|
Name: "net/http",
|
|
Packages: []string{"net/http"},
|
|
EntrypointFns: []string{"HandleFunc", "Handle", "ListenAndServe"},
|
|
HandlerType: "http_handler",
|
|
},
|
|
{
|
|
Name: "gin",
|
|
Packages: []string{"github.com/gin-gonic/gin"},
|
|
EntrypointFns: []string{"GET", "POST", "PUT", "DELETE", "PATCH", "Run"},
|
|
HandlerType: "http_handler",
|
|
},
|
|
{
|
|
Name: "echo",
|
|
Packages: []string{"github.com/labstack/echo"},
|
|
EntrypointFns: []string{"GET", "POST", "PUT", "DELETE", "PATCH", "Start"},
|
|
HandlerType: "http_handler",
|
|
},
|
|
{
|
|
Name: "fiber",
|
|
Packages: []string{"github.com/gofiber/fiber"},
|
|
EntrypointFns: []string{"Get", "Post", "Put", "Delete", "Patch", "Listen"},
|
|
HandlerType: "http_handler",
|
|
},
|
|
{
|
|
Name: "chi",
|
|
Packages: []string{"github.com/go-chi/chi"},
|
|
EntrypointFns: []string{"Get", "Post", "Put", "Delete", "Patch", "Route"},
|
|
HandlerType: "http_handler",
|
|
},
|
|
{
|
|
Name: "mux",
|
|
Packages: []string{"github.com/gorilla/mux"},
|
|
EntrypointFns: []string{"HandleFunc", "Handle", "NewRouter"},
|
|
HandlerType: "http_handler",
|
|
},
|
|
{
|
|
Name: "grpc",
|
|
Packages: []string{"google.golang.org/grpc"},
|
|
EntrypointFns: []string{"RegisterServer", "NewServer"},
|
|
HandlerType: "grpc_method",
|
|
},
|
|
{
|
|
Name: "cobra",
|
|
Packages: []string{"github.com/spf13/cobra"},
|
|
EntrypointFns: []string{"Execute", "AddCommand", "Run"},
|
|
HandlerType: "cli_command",
|
|
},
|
|
}
|
|
|
|
// DetectFramework checks if a function is related to a known framework
|
|
func DetectFramework(fn *ssa.Function) *FrameworkPattern {
|
|
if fn.Pkg == nil {
|
|
return nil
|
|
}
|
|
|
|
pkgPath := fn.Pkg.Pkg.Path()
|
|
|
|
for _, pattern := range frameworkPatterns {
|
|
for _, pkg := range pattern.Packages {
|
|
if strings.Contains(pkgPath, pkg) {
|
|
return &pattern
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DetectFrameworkEntrypoint checks if a call is a framework route registration
|
|
func DetectFrameworkEntrypoint(call *ssa.Call) *Entrypoint {
|
|
callee := call.Call.StaticCallee()
|
|
if callee == nil || callee.Pkg == nil {
|
|
return nil
|
|
}
|
|
|
|
pkgPath := callee.Pkg.Pkg.Path()
|
|
fnName := callee.Name()
|
|
|
|
for _, pattern := range frameworkPatterns {
|
|
for _, pkg := range pattern.Packages {
|
|
if strings.Contains(pkgPath, pkg) {
|
|
for _, epFn := range pattern.EntrypointFns {
|
|
if fnName == epFn {
|
|
nodeID := makeSymbolID(callee)
|
|
return &Entrypoint{
|
|
ID: nodeID,
|
|
Type: pattern.HandlerType,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsHTTPHandler checks if a function signature matches http.Handler
|
|
func IsHTTPHandler(fn *ssa.Function) bool {
|
|
sig := fn.Signature
|
|
|
|
// Check for (http.ResponseWriter, *http.Request) signature
|
|
if sig.Params().Len() == 2 {
|
|
p0 := sig.Params().At(0).Type().String()
|
|
p1 := sig.Params().At(1).Type().String()
|
|
|
|
if strings.Contains(p0, "ResponseWriter") && strings.Contains(p1, "Request") {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Check for gin.Context, echo.Context, fiber.Ctx, etc.
|
|
if sig.Params().Len() >= 1 {
|
|
p0 := sig.Params().At(0).Type().String()
|
|
if strings.Contains(p0, "gin.Context") ||
|
|
strings.Contains(p0, "echo.Context") ||
|
|
strings.Contains(p0, "fiber.Ctx") ||
|
|
strings.Contains(p0, "chi.") {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|