JSLEEKR/routecheck

GitHub: JSLEEKR/routecheck

一款跨框架 API 路由静态分析工具,在不启动服务的前提下自动检测路由冲突、遮蔽、重复注册和参数命名问题,CI 友好且零运行时依赖。

Stars: 0 | Forks: 0

[![Go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=for-the-badge&logo=go&logoColor=white)](https://golang.org) [![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)](LICENSE) [![Tests](https://img.shields.io/badge/Tests-161+-success?style=for-the-badge)](https://github.com/JSLEEKR/routecheck) [![CI-Friendly](https://img.shields.io/badge/CI-Friendly-blue?style=for-the-badge)](https://github.com/JSLEEKR/routecheck) # routecheck **API 路由的静态分析。** 跨多个 Web 框架解析路由定义,检测路由冲突、路由遮蔽、重复注册、参数名冲突以及缺失的方法。单一二进制文件,零运行时依赖,对 CI 友好。 ## 为什么会有这个项目 每一个复杂的 Web 应用都会随着时间的推移积累大量的路由定义。随着项目的增长,一些不易察觉的 Bug 会悄然出现: - **路由遮蔽**:在许多框架中,先注册 `GET /users/:id` 再注册 `GET /users/admin` 意味着 `/users/admin` 将无法被访问到 - **重复注册**:同一个 method+path 被注册两次,通常位于不同的文件中,从而导致静默覆盖 - **参数冲突**:`/users/:id` 和 `/users/:userId` 在同一位置使用了不同的参数名 - **缺失 CRUD 方法**:一个资源拥有 GET、POST、PUT,却没有 DELETE —— 这是 deliberate 还是遗忘了? 这些问题通常会在运行时被捕获(如果还能被捕获的话),并且往往发生在生产环境中。**routecheck** 通过静态分析源码在构建阶段就能捕获这些问题——无需启动服务器。 ## 支持的框架 | 框架 | 语言 | 参数风格 | 自动检测 | |-----------|----------|-------------|----------------| | **Express** | JavaScript/TypeScript | `:id` | `require('express')` / `import express` | | **Gin** | Go | `:id`, `*path` | `github.com/gin-gonic/gin` | | **FastAPI** | Python | `{id}`, `{id:int}` | `from fastapi import` | | **Chi** | Go | `{id}`, `{id:[0-9]+}` | `github.com/go-chi/chi` | | **Echo** | Go | `:id`, `*path` | `github.com/labstack/echo` | 所有参数风格在内部都会被标准化为 `{param}`,以实现一致的冲突检测。 ## 安装说明 ### 通过源码 ``` go install github.com/JSLEEKR/routecheck/cmd/routecheck@latest ``` ### 下载二进制文件 从 [GitHub Releases](https://github.com/JSLEEKR/routecheck/releases) 下载最新版本。 ### 从源码构建 ``` git clone https://github.com/JSLEEKR/routecheck.git cd routecheck go build -o routecheck ./cmd/routecheck ``` ## 快速入门 ``` # 递归扫描当前目录 routecheck . # 扫描特定文件 routecheck src/routes.js server/main.go # 使用 glob 模式扫描 routecheck "src/**/*.js" # 强制使用特定 framework parser routecheck --framework express src/ # 用于 CI 集成的 JSON 输出 routecheck --format json src/ > report.json # 列出所有发现的路由 routecheck --list src/ # Strict 模式:将警告视为错误 routecheck --strict src/ ``` ## 用法 ``` routecheck [flags] [paths...] Flags: -f, --format string Output format: text, json (default "text") --framework string Force framework parser: express, gin, fastapi, chi, echo --ext string Comma-separated file extensions to scan (e.g., .go,.js) -r, --recursive Recursively scan directories (default true) -l, --list List all discovered routes --strict Treat warnings as errors (exit code 1) -v, --version Show version Exit Codes: 0 Clean — no errors found 1 Conflicts — errors (or warnings in strict mode) detected 2 Parse error — invalid flags or file access problems ``` ## 问题类型 ### Error 严重级别 #### `duplicate` — 同一个 method + 模式被注册了多次 ``` [ERROR] Duplicate route: GET /users registered 2 times -> GET /users (routes.js:5) -> GET /users (routes.js:15) ``` #### `shadowed` — 路由不可达,因为前面的参数化路由会优先匹配 ``` [ERROR] Route GET /users/admin (line 15) is shadowed by GET /users/{id} (line 5) -> GET /users/{id} (routes.js:5) -> GET /users/admin (routes.js:15) ``` ### Warning 严重级别 #### `conflict` — 两个路由可能会匹配同一个请求 ``` [WARN] Potential conflict: GET /users/{id} vs GET /users/{userId} -> GET /users/{id} (routes.js:5) -> GET /users/{userId} (api.js:10) ``` #### `param_conflict` — 同一路径位置使用了不同的参数名 ``` [WARN] Parameter name conflict at position 1: {id} vs {userId} -> GET /users/{id}/posts (routes.js:5) -> GET /users/{userId}/posts (api.js:10) ``` ### Info 严重级别 #### `missing_method` — 资源模式拥有大部分 CRUD 方法,但缺少了某个方法 ``` [INFO] Pattern /users has 3 methods but missing DELETE — intentional? ``` ## 输出格式 ### Text(默认) 带有严重级别图标和路由位置、易于阅读的输出: ``` routecheck: scanned 12 routes ------------------------------------------------------------ ERRORS (2): [ERROR] Duplicate route: GET /health registered 2 times -> GET /health (main.go:8) -> GET /health (main.go:25) [ERROR] Route GET /users/admin (line 20) is shadowed by GET /users/{id} (line 10) -> GET /users/{id} (main.go:10) -> GET /users/admin (main.go:20) WARNINGS (1): [WARN] Potential conflict: GET /users/{id} vs GET /users/{userId} -> GET /users/{id} (main.go:10) -> GET /users/{userId} (main.go:22) ------------------------------------------------------------ Summary: 2 errors, 1 warnings, 0 info ``` ### JSON 用于 CI 集成的机器可读输出: ``` { "version": "1.0.0", "total_routes": 12, "issues": [ { "type": "duplicate", "severity": "error", "message": "Duplicate route: GET /health registered 2 times", "routes": [ { "method": "GET", "pattern": "/health", "file": "main.go", "line": 8, "framework": "gin" }, { "method": "GET", "pattern": "/health", "file": "main.go", "line": 25, "framework": "gin" } ] } ], "summary": { "errors": 2, "warnings": 1, "info": 0 }, "routes_by_method": { "GET": 8, "POST": 2, "PUT": 1, "DELETE": 1 }, "routes_by_framework": { "gin": 12 } } ``` ## CI 集成 ### GitHub Actions ``` name: Route Check on: [push, pull_request] jobs: routecheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.26' - run: go install github.com/JSLEEKR/routecheck/cmd/routecheck@latest - run: routecheck --strict --format json src/ > routecheck-report.json - name: Check for issues run: routecheck --strict src/ ``` ### GitLab CI ``` routecheck: stage: lint image: golang:1.26 script: - go install github.com/JSLEEKR/routecheck/cmd/routecheck@latest - routecheck --strict . ``` ### Pre-commit 钩子 ``` #!/bin/sh # .git/hooks/pre-commit routecheck --strict src/ if [ $? -ne 0 ]; then echo "Route conflicts detected! Fix before committing." exit 1 fi ``` ## 框架检测 routecheck 通过分析文件内容来自动检测框架: | 框架 | 检测信号 | |-----------|-------------------| | Express | `require('express')`, `import express`, `express()`, `express.Router()` | | Gin | `"github.com/gin-gonic/gin"`, `gin.Default()`, `gin.New()` | | FastAPI | `from fastapi`, `FastAPI()`, `APIRouter(` | | Chi | `"github.com/go-chi/chi"`, `chi.NewRouter()` | | Echo | `"github.com/labstack/echo"`, `echo.New()` | 文件扩展名决定了将运行哪些检测器: - `.go` — Gin, Chi, Echo - `.js`, `.ts`, `.mjs`, `.cjs` — Express - `.py` — FastAPI 在扫描没有明显框架标识的文件时,可以使用 `--framework` 来覆盖自动检测。 ## 路由组和前缀 routecheck 能够理解路由分组和前缀组合: ### Gin 路由组 ``` api := r.Group("/api") v1 := api.Group("/v1") v1.GET("/users", listUsers) // detected as /api/v1/users ``` ### Echo 路由组 ``` api := e.Group("/api") v1 := api.Group("/v1") v1.GET("/users", listUsers) // detected as /api/v1/users ``` ### FastAPI Router 前缀 ``` router = APIRouter(prefix="/items") app.include_router(router, prefix="/api/v1") @router.get("/{id}") # detected as /api/v1/items/{id} ``` ### Chi 路由组 ``` r.Route("/api/v1", func(r chi.Router) { r.Get("/users", listUsers) // detected as /api/v1/users }) ``` ## 模式标准化 所有特定框架的参数语法都会被标准化为 `{param}`: | 框架 | 输入 | 标准化结果 | |-----------|-------|------------| | Express | `/users/:id` | `/users/{id}` | | Gin | `/users/:id` | `/users/{id}` | | Gin | `/files/*filepath` | `/files/{filepath}` | | FastAPI | `/users/{id:int}` | `/users/{id}` | | Chi | `/users/{id:[0-9]+}` | `/users/{id}` | | Echo | `/users/:id` | `/users/{id}` | | Echo | `/files/*filepath` | `/files/{filepath}` | 尾部斜杠会被剥离(根路径 `/` 除外)。会确保包含前导斜杠。 ## 跨框架分析 routecheck 能够扫描同时使用多个框架的项目——例如,一个使用 Gin 的 Go API 服务器与一个使用 Express 的 Node.js BFF 并存。来自所有框架的路由将被组合在一起进行统一分析。 ``` # 同时扫描 Go 和 JS 源码 routecheck server/ frontend/api/ ``` 这可以捕获跨框架的冲突,例如当 Express BFF 路由和 Gin 后端路由在反向代理后提供相同的路径前缀时,前者会遮蔽后者。 ## 跳过的目录 在递归扫描时,以下目录会被自动跳过: - 隐藏目录(`.git`、`.vscode` 等) - `node_modules` - `vendor` - `__pycache__` ## 架构 ``` cmd/routecheck/ CLI entry point internal/ parser/ Framework-specific route extractors express.go Express.js parser gin.go Gin parser fastapi.go FastAPI parser chi.go Chi parser echo.go Echo parser detect.go Auto-detection logic types.go Shared types (Route, ParseResult, Parser) trie/ Route trie data structure trie.go Insert, walk, path segmentation analyzer/ Conflict detection engine analyzer.go Duplicate, conflict, shadow, param, missing checks framework/ File scanning and route collection scanner.go Glob, directory walk, dedup, extension filter output/ Result formatting output.go Text and JSON formatters ``` ## 开发 ``` # 运行测试 go test ./... -v # 构建 go build -o routecheck ./cmd/routecheck # 针对测试数据运行 ./routecheck testdata/express/routes.js ./routecheck testdata/gin/routes.go ./routecheck testdata/fastapi/routes.py # 使用所有 frameworks 针对测试数据运行 ./routecheck testdata/ ``` ## 许可证 MIT ## 贡献 1. Fork 此仓库 2. 创建你的特性分支 (`git checkout -b feature/my-feature`) 3. 运行测试 (`go test ./...`) 4. 提交你的更改 5. 推送到该分支 6. 发起 Pull Request ## 致谢 作为 [@JSLEEKR](https://github.com/JSLEEKR) 的 [daily-challenge](https://github.com/JSLEEKR/daily-challenge) 项目的一部分构建。
标签:API安全, API安全, API路由, AST解析, AV绕过, Chi, CI友好, Echo, EVTX分析, Express, FastAPI, Gin, Go, Golang, JSON输出, JSON输出, Python, Ruby工具, SOC Prime, TypeScript, Web框架, 云安全监控, 代码安全审计, 安全插件, 安全编程, 开发工具, 开源框架, 影子路由, 持续集成, 无后门, 日志审计, 路由冲突检测, 路由安全, 重复路由, 静态代码扫描, 静态分析