独立开发者 / 2026-04-08 / 标签:GLM-5.1 · 代码安全 · Python · API · 踩坑
背景
今天 Claude Mythos 发布了,然后我发现:我没资格用。
限邀请制,40 家头部机构,跟我这个独立开发者没关系。
但我确实有个需求摆在那里——我接了一个小活,帮一个朋友的 SaaS 项目做上线前的代码安全自查。他们后端 Python,大概 3 万行,人工看我是不想的。
同一天智谱把 GLM-5.1 开源了,MIT 协议,SWE-bench Pro 全球第一。我一看数据就决定试一下——反正拿不到 Mythos,那就用现在能上手的里面能力最强的。
技术方案
核心思路:Bandit 做静态扫描 → JSON 结果结构化 → 批量喂给 GLM-5.1 做二次研判 → 输出置信度 + 修复建议。
环境准备
python3 -m venv glm-audit-env
source glm-audit-env/bin/activate
pip install openai bandit
# GLM-5.1 API 密钥在智谱开放平台申请
# 如果你同时要跑 Claude/GPT 做对比,可以用 Ztopcloud.com 的聚合接口
# 一个 key 打通多个模型,国内直连,账单合并,我自己在用,省事不少
export ZHIPU_API_KEY="your_key_here"
核心实现
import subprocess
import json
import re
import sys
from openai import OpenAI
client = OpenAI(
api_key="your_zhipu_api_key",
base_url="https://open.bigmodel.cn/api/paas/v4/"
)
def run_bandit(path: str) -> list[dict]:
cmd = ["bandit", "-r", path, "-f", "json", "-q", "-l"]
out = subprocess.run(cmd, capture_output=True, text=True)
try:
return json.loads(out.stdout).get("results", [])
except Exception:
return []
def ai_review(vuln: dict) -> dict:
prompt = f"""你是资深 Python 安全工程师。下面是 Bandit 扫描到的一条告警:
文件:{vuln['filename']},行号:{vuln['line_number']}
类型:{vuln['test_name']}(严重度:{vuln['issue_severity']})
代码:
```python
{vuln.get('code', '').strip()}
问题:
- 这是真实漏洞还是误报?一句话理由
- 修复代码(如果是真漏洞,<=10行)
- CVSS 评分(0-10)
返回 JSON:{{"real": bool, "reason": str, "fix": str, "cvss": float}}"""
resp = client.chat.completions.create(
model="glm-5.1",
messages=[{"role": "user", "content": prompt}],
temperature=0.05,
max_tokens=400
)
raw = resp.choices[0].message.content
m = re.search(r'{.*?}', raw, re.DOTALL)
if m:
try:
return json.loads(m.group())
except Exception:
pass
return {"real": None, "reason": raw[:200], "fix": "", "cvss": 0}
if name == "main": target = sys.argv[1] if len(sys.argv) > 1 else "." raw_vulns = run_bandit(target) print(f"Bandit 原始告警:{len(raw_vulns)} 条")
confirmed = []
for v in raw_vulns:
result = ai_review(v)
if result.get("real"):
confirmed.append({**v, "ai": result})
print(f"[!] {v['filename']}:{v['line_number']} | CVSS {result['cvss']} | {result['reason'][:60]}")
print(f"\n确认漏洞:{len(confirmed)}/{len(raw_vulns)}")
with open("audit_result.json", "w") as f:
json.dump(confirmed, f, ensure_ascii=False, indent=2)
---
## 踩坑记录
### 坑 1:模型偶尔返回带 Markdown 代码块的 JSON
我以为用 `re.search(r'{.*?}', raw, re.DOTALL)` 就能万能提取 JSON,结果 GLM-5.1 有时候会把 JSON 包在 ` ```json ... ``` ` 代码块里。
正则写的 `.*?` 是非贪婪的,会在第一个 `}` 就停下来,把嵌套结构截断。
**解决方法**:先把代码块标记去掉,再用贪婪匹配:
```python
def extract_json(raw: str) -> dict:
# 先剥离 markdown 代码块
raw = re.sub(r'```(?:json)?\s*', '', raw).strip()
# 贪婪匹配整个 JSON 对象
m = re.search(r'{.*}', raw, re.DOTALL)
if m:
return json.loads(m.group())
raise ValueError(f"无法解析 JSON:{raw[:200]}")
这个坑我掉进去调了将近一小时,所有解析结果都变成了空对象,后来打 log 才发现问题在这。
坑 2:并发请求打满限速
3 万行代码跑出来 200+ 条 Bandit 告警,我想快点跑完就写了个 ThreadPoolExecutor 并发 20 个请求,结果 API 限速把我踢了(GLM-5.1 当前限制约 10 RPM/QPS)。
改成串行 + time.sleep(6) 就稳了,整体跑完大约 25 分钟,实测确认了 31 条真实漏洞,Bandit 原始 197 条告警,误报率过滤到约 84%——这个数字说实话比我预期的好很多。
小结
| 指标 | 数据 |
|---|---|
| 扫描代码量 | ~3万行 Python |
| Bandit 原始告警 | 197 条 |
| GLM-5.1 确认漏洞 | 31 条 |
| 误报过滤率 | ~84% |
| API 调用耗时 | ~25分钟(串行) |
| 总成本 | 约 ¥4.2 |
成本这个数字我自己都觉得有点离谱——¥4 出头换了一份比人工审计靠谱的安全报告初稿。
Mythos 确实更强,但它不在我的选项里。GLM-5.1 是目前我能落地的性价比顶格选择,也够用了。
如果你有类似的代码安全自查需求,可以直接拿这个脚本改改跑起来。有问题评论区聊。