JSLEEKR/routecheck
GitHub: JSLEEKR/routecheck
一款跨框架 API 路由静态分析工具,在不启动服务的前提下自动检测路由冲突、遮蔽、重复注册和参数命名问题,CI 友好且零运行时依赖。
Stars: 0 | Forks: 0
[](https://golang.org)
[](LICENSE)
[](https://github.com/JSLEEKR/routecheck)
[](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框架, 云安全监控, 代码安全审计, 安全插件, 安全编程, 开发工具, 开源框架, 影子路由, 持续集成, 无后门, 日志审计, 路由冲突检测, 路由安全, 重复路由, 静态代码扫描, 静态分析