ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从零搭建 security-audit-skill:coding-agent 安全审计技能包实战

从零搭建 security-audit-skill:coding-agent 安全审计技能包实战

1. 从零搭建 security-audit-skill:一个面向 coding-agent 的安全审计技能包

最近半年我一直在折腾 coding-agent 相关的工具链,从代码补全到自动化重构都试了一圈,但真正让我觉得“这东西能救命”的,是安全审计这一块。security-audit-skill这个项目就是在这个背景下攒出来的——它不是一个独立的安全扫描器,而是一套给 coding-agent 用的技能包,让 agent 在写代码、改代码的过程中,能顺手把安全审计这件事做了,并且把结果落成两个关键文件:findings.json和coverage-ledger.json。

说白了,这东西解决的是一个很现实的问题:现在用 coding-agent 写代码的人越来越多,但 agent 本身对安全问题的敏感度参差不齐。你让它写个接口,它可能给你写出一个 SQL 拼接;你让它处理用户输入,它可能忘了做转义。人工 review 当然能兜底,但 review 的人不一定懂安全,懂安全的人不一定有时间逐行看。security-audit-skill的思路就是把这套审计逻辑封装成 agent 可以调用的技能,让审计变成编码流程的一部分,而不是事后补的作业。

这套东西适合谁用?如果你已经在用 coding-agent 做日常开发,或者你在团队里负责代码质量与安全基线,再或者你单纯想搞清楚 agent 到底能帮你在安全上做到什么程度,那这篇内容应该能给你一些可以直接抄的作业。我会从整体设计思路讲到具体实现,再到实操中踩过的坑,尽量把每个决策背后的“为什么”说清楚。

2. 整体设计与思路拆解

2.1 为什么不做成独立扫描器,而是做成 skill

一开始我也想过直接写个独立的扫描脚本,跑一遍代码库输出报告就完事。但实际用下来发现两个问题:第一,独立扫描器跟编码流程是割裂的,开发者写完代码不会主动去跑,跑了也不一定看报告;第二,独立扫描器很难拿到 agent 在生成代码时的上下文,比如它为什么这么写、依赖了哪些外部输入,这些信息对判断风险等级很关键。

做成 skill 就不一样了。coding-agent 在生成或修改代码时,可以主动调用这个 skill,把当前变更的代码片段、相关的上下文信息一起传进来。skill 内部做审计分析,输出结构化的 findings,agent 再根据 findings 决定是直接修复、还是提示用户确认。整个过程是嵌入在编码循环里的,不需要额外触发。

提示:skill 的调用时机很关键。我试过在 agent 每次生成代码后都调用,结果发现噪音太大,很多低风险项会打断编码节奏。后来改成只在涉及敏感操作(文件读写、网络请求、数据库操作、权限判断)时才触发,效果好很多。

2.2 findings.json 和 coverage-ledger.json 的分工

这两个文件是整个 skill 的输出核心,但它们的定位完全不同。

findings.json记录的是“发现了什么问题”。每一条 finding 包含:问题类型、严重等级、所在文件与行号、问题描述、修复建议、以及一个 confidence 字段表示审计的置信度。这个文件是给人和 agent 共同看的,agent 可以据此自动修复,人可以用来做 review 和追踪。

coverage-ledger.json记录的是“审计覆盖了什么”。它不关心具体问题,而是记录哪些文件、哪些函数、哪些代码路径被审计过了,哪些没有。这个文件的价值在于,它能告诉你审计的盲区在哪里。没有 coverage ledger 的话,你看到 findings.json 是空的,可能会误以为代码没问题,实际上可能是根本没审到。

文件核心作用关键字段消费方
findings.json记录发现的安全问题type, severity, location, description, suggestion, confidenceagent 自动修复、人工 review
coverage-ledger.json记录审计覆盖范围file, function, audited, reason, timestamp质量看板、审计报告

2.3 审计规则的组织方式

规则这块我纠结了很久。一开始想用 YAML 配置,后来发现规则之间有很多逻辑关联,纯配置表达起来很别扭。最后选的是“规则即函数”的方式,每条规则是一个独立的 Python 函数,接收代码上下文,返回 findings 列表。这样做的好处是规则可以任意复杂,可以调用外部工具,可以做跨文件分析。

规则按类别分组:注入类、认证授权类、敏感数据类、配置类、依赖类。每个类别下有多条规则,规则之间有优先级。高优先级的规则先跑,如果发现了 critical 级别的问题,低优先级的规则可以选择跳过,节省审计时间。

# 规则函数的基本签名 def check_sql_injection(context: AuditContext) -> list[Finding]: """ 检测 SQL 拼接风险。 context 包含:代码片段、AST、文件路径、依赖信息 """ findings = [] # 遍历 AST 找字符串拼接出现在 SQL 执行位置的情况 for node in context.ast.walk(): if is_sql_execution(node) and has_string_concat(node): findings.append(Finding( type="sql_injection", severity="critical", location=node.location, description="检测到 SQL 语句通过字符串拼接构造", suggestion="使用参数化查询替代字符串拼接", confidence=0.9 )) return findings

2.4 与 coding-agent 的集成方式

集成这块要考虑 agent 的多样性。不同的 coding-agent 框架,调用 skill 的方式不一样。我的做法是提供一个统一的接口层,agent 只需要实现一个invoke_skill(skill_name, payload)的方法,剩下的适配工作由 skill 内部的 adapter 处理。

payload 里包含:当前变更的代码、变更类型(新增/修改/删除)、文件路径、项目根目录、以及可选的依赖清单。skill 返回 findings 和 coverage 更新。agent 拿到 findings 后,可以根据 severity 决定行为:critical 直接阻断并提示修复,high 提示但不阻断,medium 和 low 记录到 findings.json 供后续处理。

3. 核心细节解析与实操要点

3.1 审计上下文的构建

上下文构建是审计准确率的基础。如果上下文不完整,规则要么漏报要么误报。我定义的 AuditContext 包含以下几层信息:

第一层是代码层,包括当前文件的完整内容、变更的 diff、以及 AST 解析结果。AST 用 Python 内置的 ast 模块解析,对于非 Python 文件,用 tree-sitter 做通用解析。第二层是项目层,包括项目根目录、依赖清单、配置文件内容。第三层是历史层,包括这个文件之前的审计记录、已知的误报标记。

注意:AST 解析失败的情况一定要处理。我遇到过 agent 生成的代码有语法错误,ast.parse 直接抛异常,导致整个审计流程中断。后来加了 try-except,解析失败时降级到正则匹配,同时把这条记录写进 coverage-ledger,标记为“解析失败,审计不完整”。

3.2 规则引擎的执行流程

规则引擎的执行分三个阶段:预处理、规则执行、后处理。

预处理阶段做几件事:解析代码、构建上下文、加载规则配置、确定本次审计的范围。规则执行阶段按优先级依次调用规则函数,每条规则的执行结果实时写入 findings 列表。后处理阶段做去重、排序、置信度校准,最后生成 findings.json 和 coverage-ledger.json。

去重这块有个细节:不同规则可能对同一处代码报出相似的问题。比如 SQL 注入规则和输入校验规则可能都指向同一个未校验的参数。我的做法是按 location 和 type 做联合去重,保留 severity 最高的那条,其他的合并到 related_findings 字段里。

def deduplicate(findings: list[Finding]) -> list[Finding]: """按位置和类型去重,保留最高严重等级""" grouped = {} for f in findings: key = (f.location.file, f.location.line, f.type) if key not in grouped or severity_rank(f.severity) > severity_rank(grouped[key].severity): grouped[key] = f return list(grouped.values())

3.3 coverage-ledger 的粒度控制

coverage-ledger 的粒度太粗没意义,太细又会导致文件膨胀。我最后选的粒度是“函数级 + 文件级”双层记录。文件级记录这个文件是否被审计过,函数级记录每个函数是否被审计、审计时用了哪些规则、有没有跳过。

对于没有函数结构的文件(比如配置文件、Markdown),只记录文件级。对于超大文件,如果函数数量超过阈值,会做采样审计,并在 ledger 里标记采样比例。

粒度记录内容适用场景文件大小影响
文件级文件路径、审计时间、规则集、是否完整所有文件小
函数级函数名、起止行、审计规则、跳过原因代码文件中
行级行号、审计状态仅 critical 文件大

3.4 置信度校准的实操方法

confidence 字段是我踩坑最多的地方。一开始所有规则都返回固定置信度,结果 agent 拿到一堆 0.9 的 finding,不知道该信哪个。后来改成动态校准:基础置信度由规则本身决定,然后根据上下文完整度、AST 解析成功率、历史误报率做调整。

具体公式是:final_confidence = base_confidence * context_factor * history_factor。context_factor 在上下文完整时为 1.0,缺失关键信息时降到 0.7。history_factor 根据这个规则在这个项目上的历史误报率动态调整,误报率高的规则会被降权。

提示:history_factor 需要持久化存储。我在项目根目录下建了一个.audit-history.json,记录每条规则的历史表现。这个文件不纳入版本控制,每个开发者本地维护自己的。

4. 实操过程与核心环节实现

4.1 环境准备与依赖安装

先把基础环境搭起来。Python 3.10 以上,因为用到了 match 语法和新的 typing 特性。核心依赖就三个:tree-sitter做通用 AST 解析,pydantic做数据模型校验,pyyaml读规则配置。其他的尽量用标准库,减少依赖冲突。

pip install tree-sitter pydantic pyyaml

目录结构这样组织:

security-audit-skill/ ├── skill/ │ ├── __init__.py │ ├── context.py # AuditContext 定义 │ ├── engine.py # 规则引擎 │ ├── rules/ # 规则目录 │ │ ├── injection.py │ │ ├── auth.py │ │ ├── secrets.py │ │ └── config.py │ ├── output.py # findings/coverage 输出 │ └── adapter.py # agent 适配层 ├── tests/ ├── config/ │ └── rules.yaml # 规则优先级与开关 └── README.md

4.2 核心数据模型定义

数据模型用 pydantic 定义,保证序列化和校验的可靠性。Finding 和 CoverageEntry 是两个核心模型。

from pydantic import BaseModel, Field from typing import Literal, Optional from datetime import datetime class Location(BaseModel): file: str line: int column: int = 0 end_line: Optional[int] = None class Finding(BaseModel): id: str type: str severity: Literal["critical", "high", "medium", "low", "info"] location: Location description: str suggestion: str confidence: float = Field(ge=0.0, le=1.0) related_findings: list[str] = [] rule_name: str timestamp: datetime = Field(default_factory=datetime.now) class CoverageEntry(BaseModel): file: str function: Optional[str] = None audited: bool rules_applied: list[str] = [] skip_reason: Optional[str] = None timestamp: datetime = Field(default_factory=datetime.now)

4.3 规则引擎的完整实现

引擎的核心是AuditEngine类,它负责加载规则、执行审计、生成输出。执行流程我拆成了几个方法,方便单独测试。

class AuditEngine: def __init__(self, config_path: str): self.rules = self._load_rules(config_path) self.findings: list[Finding] = [] self.coverage: list[CoverageEntry] = [] def audit(self, context: AuditContext) -> AuditResult: # 预处理 self._prepare(context) # 按优先级执行规则 for rule in self.rules: if self._should_skip(rule, context): self._record_skip(rule, context) continue try: results = rule.func(context) self.findings.extend(results) self._record_coverage(rule, context) except Exception as e: self._record_error(rule, context, e) # 后处理 self.findings = deduplicate(self.findings) self.findings = calibrate_confidence(self.findings, context) return AuditResult( findings=self.findings, coverage=self.coverage )

_should_skip的逻辑是:如果规则标记为“仅 critical 文件运行”,而当前文件不是 critical,就跳过。如果规则依赖的上下文信息缺失,也跳过并记录原因。

4.4 findings.json 的生成与格式化

输出这块要考虑可读性和机器可解析性的平衡。findings.json 用缩进 2 的格式,方便人直接看。每条 finding 的 id 用{file_hash}-{line}-{type}的格式生成,保证唯一性。

def write_findings(findings: list[Finding], output_path: str): data = { "version": "1.0", "generated_at": datetime.now().isoformat(), "total": len(findings), "by_severity": count_by_severity(findings), "findings": [f.model_dump() for f in findings] } with open(output_path, "w", encoding="utf-8") as fp: json.dump(data, fp, indent=2, ensure_ascii=False)

by_severity 这个汇总字段很实用,agent 可以快速判断本次审计的整体风险等级,决定是否需要人工介入。

4.5 coverage-ledger.json 的增量更新

coverage ledger 不能每次全量重写,那样会丢失历史记录。我的做法是增量更新:读取已有的 ledger,把本次审计的条目合并进去,同一个文件同一函数的记录以最新时间为准。

def update_coverage(new_entries: list[CoverageEntry], ledger_path: str): if os.path.exists(ledger_path): with open(ledger_path, "r") as fp: existing = json.load(fp) else: existing = {"entries": []} # 用 (file, function) 做 key 合并 merged = {(e["file"], e.get("function")): e for e in existing["entries"]} for entry in new_entries: key = (entry.file, entry.function) merged[key] = entry.model_dump() existing["entries"] = list(merged.values()) existing["last_updated"] = datetime.now().isoformat() with open(ledger_path, "w") as fp: json.dump(existing, fp, indent=2)

4.6 与 coding-agent 的对接实操

对接这块我写了一个通用的 adapter,agent 只需要实现两个回调:get_current_context()返回当前代码上下文,on_findings(findings)处理审计结果。

class SkillAdapter: def __init__(self, engine: AuditEngine): self.engine = engine def invoke(self, agent_context: dict) -> dict: context = AuditContext.from_agent(agent_context) result = self.engine.audit(context) write_findings(result.findings, "findings.json") update_coverage(result.coverage, "coverage-ledger.json") return { "findings": [f.model_dump() for f in result.findings], "summary": result.summary() }

agent 拿到返回值后,根据 summary 里的 critical_count 决定是否阻断。我实测下来,critical 阻断、high 提示、medium 和 low 静默记录,这个策略在开发效率和安全性之间平衡得比较好。

5. 常见问题与排查技巧实录

5.1 误报太多导致 agent 频繁打断

这是最常见的问题。原因通常是规则太激进,或者上下文不完整导致规则误判。排查思路:先看 findings.json 里哪些规则的误报最多,然后针对性调整。

问题现象可能原因排查方法解决方案
同一位置反复报同一问题去重逻辑失效检查 location 字段是否一致修正 location 生成逻辑
低风险代码报 critical规则阈值过严查看规则的基础置信度调整 severity 映射
配置文件被误报规则未排除非代码文件检查文件类型过滤在规则里加文件类型判断
历史误报未降权history_factor 未生效检查 .audit-history.json确认历史文件读写正常

提示:误报处理有个原则——宁可漏报不可误报。因为误报会消耗开发者对工具的信任,一旦信任没了,工具就废了。漏报可以通过 coverage ledger 来兜底,至少你知道哪里没审到。

5.2 AST 解析失败的处理

agent 生成的代码有时候语法不完整,AST 解析会失败。我的处理策略是三级降级:第一级用 ast.parse,失败后第二级用 tree-sitter 的容错解析,再失败就第三级用正则做粗粒度匹配。

降级后要在 coverage ledger 里标记parse_degraded: true,这样你知道这次审计的置信度是打折的。我遇到过一种情况:agent 生成了一段有语法错误的代码,正则匹配没发现问题,但实际那段代码逻辑上是有安全风险的。所以降级后的审计结果,confidence 要乘以 0.6 的系数。

5.3 coverage ledger 膨胀过快

函数级记录在大型项目里会导致 ledger 文件迅速膨胀。我试过在一个 5000 行的项目上跑,ledger 直接到了 2MB。后来加了几个优化:只记录最近 30 天的审计记录,超过 30 天的做归档;对于连续多次审计结果相同的函数,只保留最新一条;对于 info 级别的覆盖记录,做采样存储。

def compact_ledger(ledger_path: str, max_age_days: int = 30): """压缩 ledger,移除过期和冗余记录""" with open(ledger_path) as fp: data = json.load(fp) cutoff = datetime.now() - timedelta(days=max_age_days) entries = [ e for e in data["entries"] if datetime.fromisoformat(e["timestamp"]) > cutoff ] # 按 (file, function) 去重,保留最新 seen = {} for e in sorted(entries, key=lambda x: x["timestamp"], reverse=True): key = (e["file"], e.get("function")) if key not in seen: seen[key] = e data["entries"] = list(seen.values()) with open(ledger_path, "w") as fp: json.dump(data, fp, indent=2)

5.4 agent 不调用 skill 怎么办

这是集成层面的问题。agent 可能因为 prompt 里没提、或者 skill 注册失败而不调用。排查步骤:先确认 skill 是否在 agent 的技能列表里,再看 agent 的日志里有没有 skill 调用记录,最后检查 payload 格式是否符合预期。

我踩过的一个坑是:skill 的返回格式跟 agent 期望的不一致,agent 拿到返回值后解析失败,静默丢弃了。后来在 adapter 里加了严格的 schema 校验,返回前先自己校验一遍,确保格式正确。

5.5 审计性能优化

全量审计一个中型项目,如果规则多、文件大,耗时会比较明显。我实测下来,100 个文件、每个文件 200 行的项目,全量审计大概 8 秒。优化到 2 秒以内做了这几件事:规则并行执行(用 concurrent.futures)、AST 解析结果缓存、只审计变更文件而非全量。

注意:并行执行规则时要注意规则之间的依赖关系。有些规则依赖前一条规则的输出,这种就不能并行。我在规则配置里加了depends_on字段来声明依赖,引擎根据依赖关系构建执行图。

6. 规则编写与扩展的实操心得

6.1 写一条新规则的完整流程

假设要加一条检测硬编码密钥的规则。第一步是确定检测目标:什么样的代码模式算硬编码密钥。我定义的模式是:变量名包含 password、secret、key、token 等关键词,且赋值为字符串字面量,且字符串长度大于 8。

第二步是写规则函数。用 AST 遍历赋值节点,检查变量名和值的类型。第三步是测试,用几个正例和反例验证。第四步是注册到 rules.yaml,设置优先级和适用文件类型。

def check_hardcoded_secret(context: AuditContext) -> list[Finding]: SENSITIVE_NAMES = {"password", "secret", "key", "token", "apikey"} findings = [] for node in ast.walk(context.ast): if isinstance(node, ast.Assign): for target in node.targets: if isinstance(target, ast.Name): name_lower = target.id.lower() if any(s in name_lower for s in SENSITIVE_NAMES): if isinstance(node.value, ast.Constant) and isinstance(node.value.value, str): if len(node.value.value) > 8: findings.append(Finding( type="hardcoded_secret", severity="high", location=Location( file=context.file_path, line=node.lineno ), description=f"变量 {target.id} 疑似硬编码密钥", suggestion="将密钥移至环境变量或密钥管理服务", confidence=0.85, rule_name="check_hardcoded_secret" )) return findings

6.2 规则优先级的设置逻辑

优先级不是拍脑袋定的,我按“影响面 × 利用难度”来排。影响面大且利用难度低的,优先级最高,比如 SQL 注入、命令注入。影响面大但利用难度高的,优先级次之,比如反序列化漏洞。影响面小但利用难度低的,优先级再次,比如信息泄露。

规则类别优先级执行时机跳过条件
注入类P0最先执行从不跳过
认证授权类P1注入类之后文件不含认证逻辑时跳过
敏感数据类P1认证类之后文件不含数据处理时跳过
配置类P2数据类之后非配置文件跳过
依赖类P2配置类之后无依赖清单时跳过

6.3 规则测试的实操方法

每条规则都要有对应的测试用例。我用 pytest 组织测试,每个规则一个测试文件,包含正例(应该报出问题)和反例(不应该报出问题)。正例至少 3 个,覆盖不同的代码写法。反例至少 5 个,覆盖常见的误报场景。

def test_sql_injection_positive(): code = ''' cursor.execute("SELECT * FROM users WHERE id = " + user_id) ''' context = build_context(code) findings = check_sql_injection(context) assert len(findings) == 1 assert findings[0].severity == "critical" def test_sql_injection_negative(): code = ''' cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) ''' context = build_context(code) findings = check_sql_injection(context) assert len(findings) == 0

6.4 规则版本管理与兼容性

规则会随着时间演进,新版本可能改变检测逻辑,导致历史 findings 无法对比。我的做法是给每条规则加版本号,findings 里记录规则版本。这样在对比不同时间的审计结果时,能知道差异是代码变了还是规则变了。

规则版本用语义化版本,major 版本变更表示检测逻辑有破坏性改变,minor 表示新增检测能力,patch 表示修复误报。每次规则变更都要更新版本号并记录 changelog。

7. 审计结果的分析与利用

7.1 findings.json 的消费方式

findings.json 生成后,有几类消费方式。第一类是 agent 自动修复,对于 confidence 高且修复方案明确的 finding,agent 可以直接改代码。第二类是人工 review,开发者打开 findings.json 逐条看,决定修复还是标记为误报。第三类是聚合分析,把多个项目的 findings 汇总,看哪类问题最普遍。

我实测下来,agent 自动修复的成功率大概在 70% 左右。失败的 30% 主要是修复方案不明确,或者修复会引入新的问题。所以我的策略是:critical 和 high 级别的 finding,agent 只提示不自动修复;medium 和 low 级别的,agent 可以尝试自动修复,但修复后要重新审计确认。

7.2 coverage-ledger.json 的看板化

coverage ledger 单独看很枯燥,但做成看板就很有价值。我用一个简单的脚本把 ledger 转成 HTML 报告,展示每个文件的审计覆盖率、未审计的函数列表、审计降级的文件列表。

def generate_coverage_report(ledger_path: str, output_path: str): with open(ledger_path) as fp: data = json.load(fp) total = len(data["entries"]) audited = sum(1 for e in data["entries"] if e["audited"]) degraded = sum(1 for e in data["entries"] if e.get("parse_degraded")) # 生成 HTML...

这个报告在团队周会上特别有用,能直观看到哪些模块的审计覆盖不到位,需要补审。

7.3 审计数据的长期追踪

单次审计的结果价值有限,长期追踪才能看出趋势。我把每次审计的汇总数据(总 finding 数、各 severity 数量、覆盖率)追加到一个时间序列文件里,用简单的折线图展示趋势。如果发现某类 finding 数量突然上升,说明最近的代码变更引入了新的风险模式,需要重点关注。

提示:时间序列数据不要存在 findings.json 里,单独建一个audit-trend.jsonl,每行一条记录,方便追加和解析。

8. 实操中踩过的坑与独家避坑技巧

8.1 坑一:规则之间的相互干扰

有次我加了一条检测“未使用参数化查询”的规则,结果它跟已有的 SQL 注入规则产生了冲突。同一条代码,SQL 注入规则报 critical,新规则报 medium,两条 finding 指向同一位置但描述不同。agent 拿到后不知道该信哪个,修复时改了一半就停了。

解决办法是在规则设计阶段就明确边界:一条规则只检测一类问题,不同规则之间不能有重叠的检测范围。如果确实需要从不同角度检测同一处代码,那就在后处理阶段做合并,而不是让两条规则各自输出。

8.2 坑二:coverage ledger 的并发写入

多个 agent 实例同时审计同一个项目时,coverage ledger 会出现并发写入冲突。我遇到过两次 ledger 文件损坏,都是因为两个进程同时写。后来加了文件锁,写入前先获取锁,写完释放。

import fcntl def safe_update_coverage(entries, ledger_path): with open(ledger_path, "a+") as fp: fcntl.flock(fp, fcntl.LOCK_EX) try: # 读取、合并、写入 ... finally: fcntl.flock(fp, fcntl.LOCK_UN)

8.3 坑三:confidence 校准的冷启动

history_factor 依赖历史数据,但新项目没有历史,所有规则的 history_factor 都是初始值。这导致新项目的 confidence 校准不准,要么过于自信要么过于保守。我的做法是给新项目设置一个 7 天的观察期,观察期内 history_factor 固定为 0.8,观察期结束后根据实际误报率动态调整。

8.4 坑四:agent 的上下文窗口限制

coding-agent 的上下文窗口有限,如果一次性把整个项目的代码都传给 skill,会超出窗口限制。我的做法是只传变更文件的代码加上必要的依赖信息,不传全量代码。对于需要跨文件分析的规则,在 skill 内部按需读取文件,而不是让 agent 传过来。

8.5 坑五:审计结果的时效性

代码变更后,之前的审计结果就失效了。但 coverage ledger 里还留着旧记录,导致覆盖率虚高。我的做法是在 ledger 里记录代码的 hash,审计前先比对 hash,hash 变了就标记为“待重新审计”,覆盖率计算时排除这些记录。

坑位现象根因解决方案
规则干扰同一位置多条冲突 finding规则检测范围重叠明确规则边界,后处理合并
并发写入ledger 文件损坏多进程同时写文件锁
冷启动新项目 confidence 不准无历史数据观察期固定系数
上下文超限skill 调用失败传入代码过多只传变更文件
时效性覆盖率虚高旧记录未失效hash 比对标记

9. 后续可以扩展的方向

这套 skill 目前覆盖的是代码层面的静态审计,后续可以往几个方向扩展。第一个方向是运行时审计,把 skill 跟运行时监控结合,在代码执行时动态检测安全问题。第二个方向是依赖链审计,不仅审当前项目的代码,还审依赖包的已知问题。第三个方向是审计规则的自动生成,用历史 findings 训练模型,自动发现新的风险模式。

我个人最看好的是第一个方向。静态审计有天然的局限,很多安全问题只有在运行时才能暴露。如果能把静态审计和运行时监控打通,findings.json 里就能包含运行时发现的问题,coverage ledger 也能记录运行时的覆盖情况,整个审计的完整度会提升一个档次。

不过这些都是后话了,眼下先把静态审计这块做扎实。规则的质量、上下文的完整度、输出的可用性,这三件事做好了,skill 的价值就已经很实在了。我在实际项目里跑了三个月,findings.json 里累计记录了 200 多条问题,其中 critical 和 high 级别的有 30 多条,都是真实存在的风险。这个数字不算多,但每一条被提前发现,就少一次线上事故。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进