Settle exposure first. Before drafting anything about severity, find out who can reach the system: from the internet, from other internal services only, from customer applications (an SDK), or from a local operator. Look for it in deployment and ingress config, then ask. Nearly every severity depends on the answer, so if it stays unconfirmed, say so in "System and Scope", make it the first open question, and give each severity a base level plus the conditions that raise or lower it ("high; critical if reachable without Sentry in front of it").
Use the documentation from step 1 to find the controls the project intends, then confirm each one in code. Read the smallest useful set of files at the places security is usually enforced:
- the authentication entry point, and any list of paths or methods that skip it
- how caller and tenant identity is carried between services, and what happens when it is missing or invalid
- tenant scoping in data access: query helpers, lint rules, and the places that bypass them
- where untrusted code runs: sandboxes, interpreters, build or CI steps
- outbound credentials (SCM, cloud, third-party APIs) and how they are scoped and checked
- secrets handling: where secrets live, and what gets logged, traced, or returned
Add security-critical tests and prior validated findings if the user can point you to them (published advisories, a tracker, saved review output). Tests show an intended control or failure mode; they do not prove the control works.
Establish what a reviewer needs to know:
- System and scope: the product or component, deployment model (SaaS, self-hosted, SDK running in customer apps, internal tool), exposure, important assets and operations, and the paths that mark a real boundary.
- Threat model: trusted callers, attacker-controlled inputs, and trust boundaries. Be specific about each actor class, for example an unauthenticated internet caller, an authenticated member of another organization, a member with a lower role in the same organization, an SDK-supplied event payload, an integration webhook, a self-hosted operator.
- Invariants: properties that must hold, such as organization isolation, authorization before mutation, scoped tokens never widening, bounded parsing of untrusted payloads, secrets never serialized to logs or events, fail-closed behaviour. For each one, find the code that enforces it, meaning code that blocks, rejects, or raises. A control that only logs, alerts, or emits a metric is monitoring, not enforcement, however its name reads.
- Reportability and severity: what makes a broken control meaningful here, including realistic reachability, impact, and exposure. Use the severity scale the user names or the repository's review config defines (for example ); otherwise use critical, high, medium, low.
- Exclusions and limitations: components or finding classes that are not reportable and why, known gaps, compensating controls, and accepted risks.
When reviewing existing guidance, compare it with that evidence. Call out stale exposure or ownership claims, missing or conflicting boundaries, exclusions broad enough to hide a real finding, accepted risks past their review date, and new surfaces revealed by routes, tests, or prior findings. For each gap give the evidence, how it would change review results, and the smallest useful correction.
Confirm with the owner every decision that suppresses or downgrades findings: exclusions, accepted risks, "not reportable" classes, and severity lower than a review tool would assign without the policy. Never turn an inference into suppression authority or treat an unverified control as proof that a finding is safe. If the owner is unavailable, mark the item
where it appears and add the question to the open-questions list you present alongside the draft. An unresolved exclusion does not suppress anything.
Ask the user, don't just list questions. When the user is present, put open decisions to them as you reach them, rather than saving them for the end:
- Ask no more than three questions at once, starting with the ones whose answers would change the most findings. Exposure comes first.
- Use the agent's structured question tool when it has one, otherwise ask in plain text. Offer the likely answers as choices, with your proposal first, for example "Not reportable (proposed)", "Reportable", or "Not sure, leave unresolved".
- Right after the exposure question, ask once whether the user owns this code or can decide for its owners (CODEOWNERS shows who they are). Record an answer as owner-confirmed only if they can. Otherwise keep the item and note the user's view next to the question.
- Update the draft from each answer, then ask the next batch. Stop when the user says to, or when only questions the user can't answer remain.
Prefer plain questions: Which surfaces are internet-facing? Which inputs are attacker-controlled? Is this also shipped self-hosted? Are any finding classes intentionally out of scope? In a public repository, a question that hints at a possible weakness is still asked, but only in the conversation with the user or in local working notes, never in anything committed or published.
The policy describes how the system is meant to behave. Finding and judging vulnerabilities is the job of the review tools that read it. If while reading you see code that appears to break an invariant, mention it to the user, then write the invariant as intended. Do not record the suspected gap as a known limitation or accepted risk, because that would teach the review tools to ignore it.
Keep a review-only request at review until the user asks for a draft or edit.
先确定暴露面。 在起草任何严重程度相关内容之前,先弄清哪些主体能够访问系统:来自互联网、仅来自其他内部服务、来自客户应用(SDK),还是来自本地运维人员。先查看部署和 ingress 配置,然后询问用户。几乎每项严重程度都取决于答案;如果仍无法确认,应在“System and Scope”中说明,将其列为第一个 open question,并为每种严重程度提供基准级别以及提高或降低级别的条件(例如“high;如果无需经过前置 Sentry 服务即可访问,则为 critical”)。
使用第 1 步中的文档查找项目预期的控制措施,然后在代码中确认每项措施。读取安全控制通常所在位置中最小但足够的一组文件:
- 身份验证入口,以及任何绕过身份验证的路径或方法列表
- 调用方和租户身份如何在服务之间传递,以及身份缺失或无效时会发生什么
- 数据访问中的租户范围限定:查询辅助函数、lint 规则以及绕过这些限制的位置
- 不可信代码在哪里运行:sandbox、解释器、构建或 CI 步骤
- 对外凭据(SCM、云服务、第三方 API)以及其作用域和检查方式
- Secret 处理方式:Secret 存储在哪里,以及哪些内容会被记录、追踪或返回
如果用户可以指出安全关键测试和以前验证过的发现,也应将其纳入。已发布的 advisory、问题跟踪器或保存的审查输出都可以作为参考。测试能够说明预期控制或故障模式,但不能证明该控制确实有效。
确定审查者需要了解的内容:
- 系统与范围: 产品或组件、部署模式(SaaS、自托管、运行在客户应用中的 SDK、内部工具)、暴露情况、重要资产和操作,以及标志真实边界的路径。
- 威胁模型: 可信调用方、攻击者可控输入和信任边界。具体说明每类主体,例如未通过身份验证的互联网调用方、另一组织的已认证成员、同一组织中角色较低的成员、SDK 提供的事件 payload、集成 webhook、自托管运维人员。
- 不变量: 必须成立的属性,例如组织隔离、变更前授权、作用域 token 不得扩大、对不可信 payload 进行有界解析、Secret 绝不序列化到日志或事件中,以及 fail-closed 行为。对于每个不变量,找到执行它的代码,也就是能够阻止、拒绝或抛出错误的代码。仅记录、告警或发出 metric 的控制属于监控,而不是强制执行,无论其名称如何。
- 可报告性与严重程度: 在此系统中,控制失效何时具有实际意义,包括现实的可达性、影响和暴露情况。使用用户指定的严重程度标准,或仓库审查配置定义的标准,例如 ;否则使用 critical、high、medium、low。
- 排除项与限制: 不可报告的组件或发现类别及其原因、已知缺口、补偿性控制和已接受风险。
审查现有指导时,将其与上述证据进行比较。指出过时的暴露或所有权声明、缺失或冲突的边界、宽泛到可能掩盖真实发现的排除项、超过审查日期的已接受风险,以及由路由、测试或以前的发现揭示的新攻击面。对于每个缺口,给出相关证据、它会如何改变审查结果,以及最小且有效的修正。
对于每项会抑制或降低发现等级的决策,都必须获得所有者确认,包括排除项、已接受风险、“不可报告”类别,以及低于审查工具在无策略情况下会分配的严重程度。绝不能把推断变成抑制授权,也不能把未经验证的控制视为证明某项发现是安全的。如果无法联系所有者,应在相应位置将其标为
,并把问题加入与草案一同展示的 open-questions 列表。尚未解决的排除项不会抑制任何发现。
要询问用户,不要只是列出问题。 用户在场时,应在作出决定的过程中逐步询问,而不是留到最后:
- 每次最多询问三个问题,首先询问那些答案会改变最多发现的问题。暴露面优先。
- 如果 Agent 有结构化问题工具,则使用该工具;否则使用纯文本。提供可能的答案选项,并将建议答案放在首位,例如“不可报告(建议)”“可报告”或“不确定,暂不解决”。
- 紧接着暴露面问题,询问一次用户是否拥有此代码的所属权或能否代所有者作出决定(CODEOWNERS 会显示相应负责人)。只有当用户确实能够决定时,才记录为所有者已确认;否则保留 ,并在问题旁记录用户的看法。
- 根据每个答案更新草案,然后询问下一组问题。当用户要求停止,或只剩下用户无法回答的问题时停止。
优先使用直接问题:哪些入口面暴露在互联网中?哪些输入由攻击者控制?这是否也会以自托管方式发布?是否有任何发现类别被有意排除在范围之外?在公共仓库中,仍可询问可能暗示安全弱点的问题,但只能在与用户对话或本地工作笔记中提出,绝不能写入任何已提交或已发布的内容。
策略描述系统应该如何运行。发现和判定漏洞是读取策略的审查工具的工作。如果阅读时发现代码似乎违反了某个不变量,应先向用户提及,然后按照预期行为编写该不变量。不要把疑似缺口记录为已知限制或已接受风险,否则会教导审查工具忽略它。
如果请求仅涉及审查,应保持在审查阶段,直到用户要求起草或编辑。