信息流控制(IFC)标签流转实战指南:Agent Control Specification 的无状态标签流模型
【免费下载链接】agent-governance-toolkitAI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI agents. Covers 10/10 OWASP Agentic Top 10.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-governance-toolkit
Agent Control Specification(ACS)将信息流控制(Information Flow Control, IFC)实现为一种无状态(stateless)标签流策略模型:核心运行时只在干预点评估策略,自身不存储标签、不传播污染(taint),也不内置任何 IFC 检查逻辑,全部溯源(provenance)跟踪责任由宿主(host)承担。本文以 policy-engine/docs/ifc-label-flow.md 为主体,结合 Rego 参考库、Cedar 镜像、manifest 示例与一致性测试用例,完整讲解源标签如何在快照中进入、工具清单如何声明放行级别(clearance)、策略如何执行 no write down 判定、以及result_labels如何在不破坏无状态性的前提下跨轮次传递标签流。读完你将能够:理解 ACS 的 IFC 快照契约与失败关闭(fail-closed)语义、为工具正确声明clearance/security_labels、用agent_control_specification.lib.ifc(或 AGT 库存agt.ifc)编写 no write down 策略、并通过verdict_propagating实现跨轮次标签传播。
设计核心:无状态判定 + 宿主持有溯源
ACS 的 IFC 模型刻意把"判定"与"溯源"分离:
- 核心(core)负责判定:在约定的干预点(intervention point,如
pre_tool_call、input、output)评估配置好的策略,基于当次调用传入的快照数据返回 verdict。 - 宿主(host)负责溯源:宿主在数据流经 prompt、模型调用、工具结果(tool results)、记忆(memory)以及输出准备(output preparation)等环节时,为数据附加标签;在每一个 sink 处,宿主调用 ACS 并把进入该 sink 的数据标签放在
input.snapshot.ifc.source_labels中交给策略。
这一分工的直接收益是:运行时无需保存任何跨调用的状态,水平扩展、无状态部署、缓存友好,且策略结果完全由"当前快照 + 策略"决定,具备可复现性。从源码结构看,参考 Rego 库 policy/lib/ifc.rego 全部由纯函数构成(dominates、max_sensitivity、flow_allowed、verdict等),没有任何副作用或内部状态,正是这种无状态模型的实现证据。
关于无状态运行时的更完整讨论,可参见 policy-engine/docs/stateless-runtime.md 与 policy-engine/docs/snapshot-contract.md。
快照契约:ifc.source_labels与失败关闭语义
默认快照字段为ifc.source_labels,其值必须是标签字符串数组。规范 SPECIFICATION.md 第 212 行附近明确约定:
The snapshot label convention is
input.snapshot.ifc.source_labels. The value MUST be an array of label strings. Policies MUST treat a missing field, a non array value, an empty array, or an unknown label as a denial unless a host specific policy proves a lower sensitivity by other means.
也就是说,以下任何情况都必须失败关闭(fail closed),即按拒绝处理:
| 情况 | 处理 |
|---|---|
source_labels字段缺失 | 拒绝 |
| 值不是数组 | 拒绝 |
| 数组为空 | 拒绝 |
| 包含未知标签(不在格中) | 拒绝 |
| 标签之间不可比较(incomparable) | 拒绝 |
AGT 快照路径与上游库的差异(重要坑点)
需要特别注意的是,快照中标签的具体路径随宿主 SDK 表面不同而不同。上游agent_control_specification.lib.ifc库读取的是input.snapshot.ifc.*;而 AGT(Agent Governance Toolkit)宿主 SDK 按 AGT-SNAPSHOT-1.0.md §2.2 / §2.7 填充的是:
input干预点:input.snapshot.input.ifc.source_labelsoutput干预点:input.snapshot.response.ifc.result_labels
因此,如果 AGT 用户直接导入上游agent_control_specification.lib.ifc,该库会在 AGT 宿主上永远读不到标签、每次都失败关闭。这正是 AGT 提供库存替代库 policy/lib/agt_ifc.rego(包名agt.ifc)的原因——其文件头注释明确指出:AGT 用户必须导入data.agt.ifc而不是上游包。
agt.ifc除了与上游函数表面完全对齐(dominates、max_sensitivity、flow_allowed、allow、deny、verdict、verdict_propagating及其_with_lattice变体)之外,还额外提供三个便捷辅助函数:
source_labels:读取input.snapshot.input.ifc.source_labels,非数组时回退为[];result_labels:读取input.snapshot.response.ifc.result_labels,非数组时回退为[];allow_if_dominates(sink_clearance, labels):no write down 判定的 AGT 简写,内部委托给verdict。
对应的测试 policy/lib/agt_ifc_test.rego 中test_source_labels_reads_agt_input_path与test_does_not_read_upstream_ifc_path直接验证了这两条路径的差异:即使上游路径input.snapshot.ifc.source_labels下放了["secret"],AGT 辅助函数看到的依然是[]。
工具清单元数据:clearance与security_labels
工具(tool)的放行级别是manifest 元数据,声明在工具目录(tools catalog)中。规范 SPECIFICATION.md 第 186 行说明,tools是按工具名索引的目录,每个条目除类型与 ID 外不限制其他成员;一个条目可以声明clearance标签、security_labels数组以及宿主自定义字段。第 214 行进一步明确:
clearance:单个标签,命名该 sink 能接收的最大敏感性(maximum sensitivity);security_labels:描述 sink 属性或能力的标签数组(如external、controlled、retrieval)。
运行时把这两个字段直接投影进策略输入input.tool,即input.tool.clearance与input.tool.security_labels,无需核心代码改动。这是典型的"清单驱动(manifest-driven)"设计:新增工具的 IFC 属性只需改清单,不触碰运行时。
以仓库自带的 IFC 示例 examples/ifc_agent/manifest.yaml 为例:
agent_control_specification_version: 0.4.0-alpha.1 metadata: name: "ifc-agent" policies: ifc_policy: type: rego bundle: ./policy data_paths: - ../../policy/lib/ifc.rego query: data.agent_control_specification.ifc_agent.verdict intervention_points: pre_tool_call: policy_target: "$.tool_call.args" policy_target_kind: tool_args tool_name_from: "$.tool_call.name" policy: id: ifc_policy query: data.agent_control_specification.ifc_agent.pre_tool_call_verdict tools: public_egress: type: Tool id: public_egress clearance: public security_labels: [external] trusted_archive: type: Tool id: trusted_archive clearance: confidential security_labels: [controlled]注意data_paths特意只指向policy/lib/ifc.rego这一单个文件,而不是整个policy/lib/目录——因为该目录下的*_test.rego兄弟文件会被 OPA 的数据加载器一并加载(manifest 中的注释明确记录了这一点)。示例中public_egress工具仅允许接收public级数据(对外出口),trusted_archive工具允许接收至confidential级数据(受控归档)。
默认格与 Rego 参考库
库包agent_control_specification.lib.ifc提供默认的敏感性格(lattice):public < internal < confidential < secret,并在此基础上提供支配(dominance)、最大敏感性(maximum sensitivity)、允许判定与拒绝判定辅助函数。集成者可以传入自己的格数据对象(lattice data object)以支持额外标签与偏序(partial order)。
默认格在 policy/lib/ifc.rego 中定义为自反闭包形式(每个标签支配自身及其以下所有标签):
default_lattice := {"dominates": { "public": ["public"], "internal": ["public", "internal"], "confidential": ["public", "internal", "confidential"], "secret": ["public", "internal", "confidential", "secret"], }}库提供的主要函数及其语义:
| 函数 | 语义 |
|---|---|
dominates(clearance, label)/dominates_with_lattice(lattice, clearance, label) | 判断放行级别是否支配某标签(要求两个参数都是字符串,并在格中查找闭包) |
max_sensitivity(labels)/_with_lattice | 计算一组标签的最大敏感性(集合中存在一个标签支配其余所有标签) |
flow_allowed(clearance, labels)/_with_lattice | 组合判定:count(labels) > 0且 clearance 支配最大敏感性 |
allow(...)/deny(...)/verdict(...)/_with_lattice | 返回{"decision": "allow"}或拒绝判定对象 |
propagated_labels(labels)/_with_lattice | 返回[max_sensitivity(labels)](连接语义,见下文) |
verdict_propagating(...)/_with_lattice | 允许时额外携带result_labels的判定(见下文) |
dominates_with_lattice的实现通过object.get(lattice, "dominates", {})读取格对象,再在dominates[clearance]闭包列表中查找目标标签;max_sensitivity_with_lattice用every other in labels保证被选中的标签支配集合内全部其余标签。`AGT 库存库 policy/lib/agt_ifc.rego 的函数表面与上述完全一致,只是快照路径改为 AGT 路径。
violation函数统一生成拒绝判定:
violation(clearance, labels) := { "decision": "deny", "reason": "ifc_clearance_violation", "message": sprintf("IFC clearance violation for sink clearance %v and data labels %v.", [clearance, labels]), }No Write Down:sink 处的判定规则
策略在 sink 处强制no write down(禁止向下写):只有当 sink 的clearance支配传入的每一个源标签的最大敏感性时,sink 才允许接收数据;否则策略返回拒绝判定,reason为ifc_clearance_violation。规范 SPECIFICATION.md 第 218 行给出了 MUST 级要求:若标签不可比较,比较必须失败关闭;策略发出的拒绝判定应使用reason = ifc_clearance_violation且decision = deny。
一个典型的 no write down 策略(基于agt.ifc的 AGT 写法):
package my_app.ifc import data.agt.ifc import rego.v1 default verdict := {"decision": "allow"} # 读取 AGT 快照上的源标签(input 干预点) source_labels := ifc.source_labels # sink 放行级别来自清单投影 sink_clearance := input.tool.clearance # no write down:clearance 必须支配所有源标签 verdict := ifc.verdict(sink_clearance, source_labels)也可以直接用agt.ifc.allow_if_dominates(sink_clearance, labels)简写。测试 policy/lib/agt_ifc_test.rego 中:
test_clearance_dominates_data_allows:flow_allowed("secret", ["confidential"])为真,verdict("secret", ["confidential"]).decision == "allow";test_data_exceeds_clearance_denies:deny("internal", ["confidential"])返回decision == "deny"且reason == "ifc_clearance_violation";test_missing_and_empty_labels_deny_fail_closed:即使 clearance 是最高级secret,空标签数组依然拒绝;test_incomparable_labels_deny_fail_closed:自定义格public < pii、public < pci中pii与pci不可比较,flow_allowed_with_lattice(lattice, "pii", ["pci"])为假。
result_labels:无状态运行时的跨轮次标签通道
这是本模型最巧妙的部分:核心不保存任何状态,但标签流依然可以跨轮次(across turns)延续。
- 策略 MAY 在其输出中包含
result_labels——一个描述 sink 产生数据的标签字符串数组。 - 核心把这个数组原样(verbatim)返回在
verdict.result_labels中,除此之外不做任何事:不存储、不传播污染、不自行做 IFC 检查。 - 宿主负责把返回的标签与产生出来的数据(工具结果或模型输出)一起持久化,并在后续、其策略目标派生于该数据的评估中,把它们作为
ifc.source_labels再次提供。
这样,标签流的延续完全由宿主的数据生命周期驱动,运行时始终保持无状态。verdict 的 wire schema spec/schema/wire/verdict.schema.json 中result_labels的注释即描述了这一契约:核心原样返回,宿主持久化并在后续评估中重新供给,核心不存储也不传播。
规范 SPECIFICATION.md 第 296 / 308 行附近还给出两个重要补充:
- 规范化失败关闭:当
result_labels存在但不是字符串数组时,输出规范化失败,产生runtime_error:policy_output_invalid; - 宿主不得为未发生的行为传播标签:对于没有实际产生数据的判定(如
deny,包括被审批缝(approval seam)否决的可升级deny),宿主不得传播result_labels——该成员只在 sink 数据真正产出时才有意义。因此库辅助函数在非 allow 判定上省略result_labels。
库辅助:verdict_propagating的连接(join)语义
verdict_propagating与verdict_propagating_with_lattice返回一个 allow 判定,其result_labels是输入源标签的join(最大敏感性):
verdict_propagating(clearance, labels) := verdict if { not flow_allowed(clearance, labels) verdict := violation(clearance, labels) } else := {"decision": "allow", "result_labels": propagated_labels(labels)} if { flow_allowed(clearance, labels) }这些辅助函数假设敏感性格是全序的,并把结果折叠成单个支配标签——对于public < internal < confidential < secret这类有序链,这是正确默认:多个来源的数据取最高级标签,不会丢失敏感性。测试test_verdict_propagating_returns_joined_label验证:verdict_propagating("secret", ["public", "confidential", "internal"])返回decision == "allow"且result_labels == ["confidential"]。
丰富格(compartment / category)下的注意事项
如果策略运行在具有独立 compartment 或类别(例如pii与pci)的更丰富格上,且没有任何单一输入标签支配其余标签,不要使用verdict_propagating辅助函数——它们会把结果折叠为单一标签,从而丢失溯源。此时策略应直接自行发出result_labels,要么返回完整的源标签集合,要么返回显式 join 后的标签,保证溯源不丢失。这是原文档明确给出的边界条件,也是编写自定义传播策略时必须遵守的规则。
完整可运行示例:Rust + Rego 的 ifc_agent
仓库提供了一个开箱即跑的 IFC 示例 examples/ifc_agent/README.md,采用 Rego 策略包、无 annotator,在pre_tool_call干预点通过比较snapshot.ifc.source_labels与投影的工具 clearance 执行 no write down 策略。
运行方式(opa必须在PATH上):
cargo run -p agent_control_specification --example ifc_agent --quiet策略本体 examples/ifc_agent/policy/ifc_agent.rego:
package agent_control_specification.ifc_agent import data.agent_control_specification.lib.ifc import rego.v1 default verdict := {"decision": "allow"} default pre_tool_call_verdict := {"decision": "allow"} verdict := pre_tool_call_verdict if { input.intervention_point == "pre_tool_call" } source_labels := object.get(object.get(input.snapshot, "ifc", {}), "source_labels", []) sink_clearance := object.get(input.tool, "clearance", "") pre_tool_call_verdict := ifc.verdict_propagating(sink_clearance, source_labels) if { input.intervention_point == "pre_tool_call" }驱动演示 examples/ifc_agent/demo.rs 展示了完整的"零配置构建 + 标签传播"闭环:
AgentControl::from_path("manifest.yaml")零配置构建——manifest 声明了 Rego 策略包且无 annotator,from_path直接接线捆绑的 OPA 策略分发器;public数据发往public_egress:允许,且verdict.result_labels == ["public"](传播标签);- 下一轮把传播标签作为
source_labels送回,数据流向trusted_archive(clearanceconfidential支配public):再次允许,标签原样继续传播——演示了"宿主跨轮次回灌标签、运行时保持无状态"; confidential数据发往public_egress:拒绝,reason == Some("ifc_clearance_violation")。
预期判定与 manifest、策略、演示代码的对应位置,示例 README 的 "Where to look" 一节都做了标注,便于对照学习。
Cedar 镜像:预计算闭包 +containsAll
AGS 在 policy/cedar-lib/ifc.cedar 中提供了 IFC 标签流库的 Cedar 镜像。由于Cedar 无法在策略评估过程中迭代集合来计算格闭包,镜像采用"宿主预计算"策略:
- 宿主把格闭包预先计算为资源实体(resource entity)属性
clearance_dominated_labels——一个字符串集合,命名该 sink clearance 支配的每一个标签; - Cedar 策略用集合运算符
.containsAll表达 IFC 规则:当资源 clearance 没有支配快照上的每一个源标签(或标签集合为空)时,forbid触发。
该文件实现了四个 forbid 规则:ifc_clearance_violation_input(input 干预点源标签不被支配)、ifc_clearance_violation_output(output 干预点结果标签不被支配)、以及针对空标签集合的ifc_clearance_violation_empty_input/_empty_output——后两者把"空标签失败关闭"用显式 forbid 落到了 Cedar 语义里。
选择建议(文件头注释明确说明):
- 需要格灵活性(在评估时选择格)的作者,留在 Rego 库
data.agt.ifc,它暴露了dominates_with_lattice、verdict_with_lattice等按调用时传入格文档的函数; - 采用 Cedar 的作者,当格变化时需要重新加载实体(reload entities),因为闭包是预计算进实体属性的。
一致性测试与验证
IFC 行为由两层测试覆盖:
- Rego 单元测试policy/lib/agt_ifc_test.rego(上文已多次引用):覆盖支配允许、超限拒绝、不可比较失败关闭、空/缺失标签失败关闭、多标签取最大敏感性、传播连接、AGT 路径读取、上游路径不可见等全部关键语义;
- 一致性(conformance)用例tests/conformance/cases/spec-18-ifc.case-01.json 与 spec-18-ifc.case-02.json,对应规范第 18 节:
- case-01:
search工具 clearance 为internal,快照源标签["internal"],判定allow; - case-02:同样的工具与清单,快照源标签改为
["confidential"](超过 clearance),判定deny且reason = ifc_clearance_violation。
- case-01:
两个用例都声明 Rust / Python / Node SDK 为 required、.NET 为 optional,并在references中同时指向 policy/lib/ifc.rego 与本文主体文档 docs/ifc-label-flow.md,是理解规范意图最直接的对照样本。
信任边界:完整宿主插桩是前提
原文档最后一句给出了模型的信任边界:该模型依赖完整的宿主插桩。以下情形不在 ACS 的保证范围之内:
- 未插桩的数据路径(数据绕过了宿主附加标签的环节);
- 宿主侧的标签丢失(宿主没有把标签正确附加或持久化);
- 不调用 ACS 的 sink(数据在未经策略评估的出口流出)。
换句话说,ACS 的 IFC 保证是"判定时刻的正确性",而"每个数据点都被标记、每个出口都被评估"属于宿主责任。在设计系统时,应把pre_tool_call、input、output等干预点全部接入 ACS,并在数据生命周期中严格执行"产生数据 → 持久化result_labels→ 后续作为source_labels回灌"的循环,否则标签流会在未插桩处静默断裂。相关宿主义务在 SPECIFICATION.md 第 17 节有完整定义,可结合 policy-engine/docs/security-model.md 与 policy-engine/docs/stateless-runtime.md 进一步了解整体安全模型。
【免费下载链接】agent-governance-toolkitAI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI agents. Covers 10/10 OWASP Agentic Top 10.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-governance-toolkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考