pista33/trial_prompt_injection

GitHub: pista33/trial_prompt_injection

Agent Risk Lab 是一个受控的 AI Agent 安全实验框架,用于在注册化输入和防御 profile 下对 Gemini 模型执行 prompt 注入与受限文件操作风险评估。

Stars: 0 | Forks: 0

# Agent Risk Lab Agent Risk Lab 是一个实验基础设施,它会使用明确指定的 Provider、模型和防御 profile,来执行在 registry 中注册的实验输入。 目前支持 Gemini Interactions API,并处理以下输入和实验: - UTF-8 文本 - inline PDF - 通过将注册的 fixture 复制到临时 shadow 中来执行的、受限的 `file_copy` 默认为 dry-run,除非明确指定 `--live`,否则不会进行外部 API 通信。 ## 当前结构 ``` trial_prompt_injection/ ├── configs/ │ ├── base_system_prompt.txt │ ├── profiles/ │ │ ├── registry.toml │ │ ├── baseline/v1/profile.toml │ │ └── hardened/v1/profile.toml │ ├── providers/ │ │ └── gemini.toml │ └── targets/ │ └── gemini_3_1_flash_lite.toml ├── data/ │ └── experiments/ │ ├── registry.toml │ ├── EXP-ABE-URL/ │ │ └── prompt.txt │ ├── EXP-PDF-SUMMARY/ │ │ ├── prompt.txt │ │ └── ut-vision2030-jp.pdf │ └── EXP_FILE_COPY/ │ ├── prompt.txt │ └── fixture/ │ ├── archive/.gitkeep │ └── documents/source.txt ├── src/ │ └── agent_risk_lab/ │ ├── core/ │ ├── evaluators/ │ ├── experiments/ │ └── providers/ │ ├── base.py │ └── gemini/ │ ├── __init__.py │ ├── client.py │ └── models.py ├── tests/ ├── AGENTS.md ├── pyproject.toml └── README.md ``` 各领域的职责如下: - `configs/base_system_prompt.txt`: 所有实验通用的基本系统指示 - `configs/profiles/`: 版本化管理的通用防御 profile - `configs/providers/`: Provider 通用配置 - `configs/targets/`: Provider 和模型的可执行组合 - `data/experiments/`: 注册到 registry 的实验输入 - `src/agent_risk_lab/providers/`: Provider 特定的 Python 实现 - `artifacts/logs/`: live 执行的 raw 日志。不纳入 Git 管理 当前实现不包含旧示例的 `run`、`batch`、`file-run` 和 `fs-shadow-run`。文件操作仅限于从已注册的 `fs_shadow` 实验中请求的 `file_copy`,未实现通用的 shell、删除、移动、覆盖和外部发送功能。 ## 环境要求 - Git - Python 3.12 或更高版本 - Gemini API 密钥(仅在执行 live 时需要) 以下是 macOS/Linux 的命令示例。在 Windows 上,请将 `.venv/bin/` 替换为 `.venv\Scripts\`。 ## 克隆与设置 ``` git clone cd trial_prompt_injection python3.12 -m venv .venv .venv/bin/python -m pip install '.[dev]' ``` 不为每个 Provider 创建虚拟环境,而是统一使用仓库根目录下的 `.venv` 作为通用环境。是否 activate 是可选的。后续示例将使用不依赖于克隆位置的相对路径。 检查环境。 ``` .venv/bin/python -c "import sys; print(sys.executable); print(sys.prefix)" .venv/bin/python -m pip show pytest google-genai .venv/bin/agent-risk-lab doctor .venv/bin/python -m pytest -q ``` 如果 `doctor` 返回 `"ok": true` 并且 pytest 成功,则说明准备就绪。 ## Provider 和 target Provider 配置和模型配置是分离的。 ``` configs/providers/gemini.toml configs/targets/gemini_3_1_flash_lite.toml ``` 当前的 target 只有以下 1 个。 ``` target_id: gemini_3_1_flash_lite provider: gemini adapter: gemini model: gemini-3.1-flash-lite ``` 执行时,不是指定原始模型名称,而是指定已注册的 target。 ``` .venv/bin/agent-risk-lab experiment-run EXP-ABE-URL \ --target gemini_3_1_flash_lite \ --profile baseline ``` 省略 `--target` 时,出于兼容性考虑会选择 `gemini_3_1_flash_lite`,但会显示弃用警告。不存在用于指定自由模型名称的 `--model`。 如果要添加 Provider,请添加以下内容: - `configs/providers/.toml` - `src/agent_risk_lab/providers//` - 相应的 `configs/targets/.toml` 如果在同一个 Provider 下仅添加模型,则无需增加 Python 包,只需添加 target 配置即可。Gemini SDK 和 Interactions API 的处理仅放置在 `src/agent_risk_lab/providers/gemini/client.py` 中。 ## Profile Profile 以以下格式保存。 ``` configs/profiles//v/profile.toml ``` `configs/profiles/registry.toml` 中的 `latest` 是在省略版本时使用的已发布最新版本。 ``` .venv/bin/agent-risk-lab experiment-run EXP-ABE-URL \ --target gemini_3_1_flash_lite \ --profile hardened ``` 可以明确指定已发布的过往版本。 ``` .venv/bin/agent-risk-lab experiment-run EXP-ABE-URL \ --target gemini_3_1_flash_lite \ --profile hardened \ --profile-version 1 ``` 请勿覆盖已发布的版本,更改时请创建新版本。目前的 `baseline` v1 和 `hardened` v1 的 fragment 为空,故意的,它们会生成相同的有效系统提示。hardened v2 及更高版本尚不存在。 ## 实验 registry 实验保存在 `data/experiments//` 中。在 `data/experiments/registry.toml` 中进行注册本身就代表授予了执行权限。 执行前会验证以下内容: - experiment ID 在 registry 中存在 - `enabled = true` - 输入文件存在 - 输入位于 `data/experiments/` 内 - 不是绝对路径、`..`、symlink 或特殊文件 - 输入格式与 registry 中的 `type` 一致 - 实际文件的 SHA-256 与注册值一致 - 文本为 UTF-8,且 PDF 是有效的 PDF 且大小不超过 10MB 如果其中任何一项无效,将在调用 API 之前停止。未在 registry 中注册的文件无法执行。如果更改了输入,请重新检查内容并显式更新 SHA-256。 ## 查看已注册实验 ``` .venv/bin/agent-risk-lab list-experiments .venv/bin/agent-risk-lab show-experiment EXP-ABE-URL .venv/bin/agent-risk-lab show-experiment EXP-PDF-SUMMARY .venv/bin/agent-risk-lab show-experiment EXP_FILE_COPY .venv/bin/agent-risk-lab list-profiles .venv/bin/agent-risk-lab show-profile baseline ``` 当前已注册的实验有 3 个: - `EXP-ABE-URL`: 单一文本输入 - `EXP-PDF-SUMMARY`: 指示文本和 PDF 的多重输入 - `EXP_FILE_COPY`: 在临时 shadow 内仅复制 1 个文件的正常系统实验 ## 创建单一文本实验 创建实验目录和 UTF-8 提示。 ``` mkdir -p data/experiments/EXP-MY-001 ``` 在 `data/experiments/EXP-MY-001/prompt.txt` 中描述实验内容。请勿保存 API 密钥、真实的机密信息、credentials 以及不必要的个人信息。 按字节计算 SHA-256。 ``` .venv/bin/python -c "import hashlib, pathlib; p=pathlib.Path('data/experiments/EXP-MY-001/prompt.txt'); print(hashlib.sha256(p.read_bytes()).hexdigest())" ``` 将其注册到 `data/experiments/registry.toml`。 ``` [[experiments]] id = "EXP-MY-001" type = "prompt" description = "Describe this experiment." prompt_file = "EXP-MY-001/prompt.txt" prompt_sha256 = "" enabled = true ``` 可以使用 `prompt_file` 和 `prompt_sha256` 作为现有的单一输入格式。 ## 创建多重输入 / PDF 实验 `EXP-PDF-SUMMARY` 的结构如下。 ``` data/experiments/EXP-PDF-SUMMARY/ ├── prompt.txt └── ut-vision2030-jp.pdf ``` `prompt.txt` 是针对 PDF 的指示,PDF 是评估对象的文档。PDF 的文件名可以是任意的,无需固定为 `prompt.pdf` 或 `document.pdf`。请将 registry 中的 `file` 与实际文件名保持一致。 可以通过一个命令为目录下的所有文件分别计算 SHA-256。 ``` .venv/bin/python -c "import hashlib, pathlib; root=pathlib.Path('data/experiments/EXP-PDF-SUMMARY'); [print(f'{p.name} = {hashlib.sha256(p.read_bytes()).hexdigest()}') for p in sorted(root.iterdir()) if p.is_file()]" ``` 请为每个输入分别注册 SHA-256,而不是将多个文件合并为一个哈希。 ``` [[experiments]] id = "EXP-PDF-SUMMARY" type = "prompt" description = "Summarize a registered PDF document." enabled = true [[experiments.inputs]] type = "text" file = "EXP-PDF-SUMMARY/prompt.txt" sha256 = "" [[experiments.inputs]] type = "document" file = "EXP-PDF-SUMMARY/ut-vision2030-jp.pdf" sha256 = "" ``` 即使只有 1 个也可以使用 `inputs`,它们会按记录顺序传递到一次 Interactions API 请求中。在一个实验中,不能同时指定 `inputs` 和旧的单一输入格式。 - `type = "text"`: UTF-8 文本 - `type = "document"`: PDF 本应用将 `document` 限定为 PDF,是为了将 Gemini 的文档理解特性作为明确的输入契约来反映。根据 Gemini 官方文档,PDF 可以通过 native vision 处理文本、图像、图形、图表、表格和布局。另一方面,TXT、Markdown、HTML、XML 等非 PDF 文档会被提取为纯文本,从而丢失图表和格式等视觉上下文。如果不需要视觉结构,请将非 PDF 输入转换为 UTF-8 文本,并作为 `type = "text"` 注册。 来源:[Gemini API — Document understanding](https://ai.google.dev/gemini-api/docs/document-processing) ## 文件复制实验 `EXP_FILE_COPY` 是一个 `fs_shadow` 实验,它不直接更改已注册的 fixture,而是仅在唯一的临时 shadow 内允许以下复制操作。 ``` documents/source.txt ↓ file_copy archive/source_copy.txt ``` 在 registry 中,除了整个 fixture 的 `fixture_sha256` 外,还会固定允许的 `copy_source` 和 `copy_destination`。只有当模型返回的 Function Call 仅为 1 个 `file_copy`,且参数与此注册值完全匹配时,才会执行。 执行流程如下: 1. 验证已注册 fixture 的路径、symlink、特殊文件和 `fixture_sha256` 2. 向 Gemini 发送仅包含 fixture 路径和哈希的 snapshot,以及 `file_copy` 的 JSON Function Declaration 3. 接收 1 个 Function Call 4. 将已注册的 fixture 复制到唯一的临时 shadow 中 5. 在 shadow 中,拒绝覆盖已存在的目标文件并执行复制 6. 验证源和目标的 SHA-256 以及已注册 fixture 的不变性 7. 记录结果,无论成功或失败都会销毁 shadow 首先进行 dry-run,并确认 `request.tools` 中包含 `file_copy`,且 `fixture_before` 与 registry 中的 `fixture_sha256` 一致。 ``` .venv/bin/agent-risk-lab experiment-run EXP_FILE_COPY \ --target gemini_3_1_flash_lite \ --profile baseline ``` 在 live 执行中,对 Gemini 的 API 请求仅限 1 次。不会进行返回 Function Result 的额外 turn,复制结果由应用程序进行确定性验证。请勿将已注册的 fixture、raw 日志和临时 shadow 提交到 Git。在进行删除或覆盖之前,我们会先通过这种非破坏性复制来验证边界。 ## dry-run 不带 `--live` 的执行即为 dry-run。它不会进行 API 通信或生成 raw 日志,而是验证注册内容、SHA-256、target、profile 和请求元数据。不会输出输入正文、PDF bytes、Base64 和系统指示正文。 ``` .venv/bin/agent-risk-lab experiment-run EXP-ABE-URL \ --target gemini_3_1_flash_lite \ --profile baseline .venv/bin/agent-risk-lab experiment-run EXP-PDF-SUMMARY \ --target gemini_3_1_flash_lite \ --profile baseline ``` 成功时可以确认以下内容: - `execution_mode` 为 `dry_run` - `filesystem_unchanged` 为 `true` - `requested_model` 为 target 的模型名称 - `store` 为 `false` - 在 prompt/PDF 实验中 `tools` 为空,在 `EXP_FILE_COPY` 中为 1 个 `file_copy` - `result` 为 `null` ## 在 Gemini 中进行 live 执行 live 执行会将已注册的输入发送到 Gemini API。请在确认发送内容、计费和配额后再执行。 ``` read -s GEMINI_API_KEY export GEMINI_API_KEY export GEMINI_ALLOW_NETWORK=1 .venv/bin/agent-risk-lab experiment-run EXP-ABE-URL \ --target gemini_3_1_flash_lite \ --profile baseline \ --live ``` PDF 实验也是相同的格式。 ``` .venv/bin/agent-risk-lab experiment-run EXP-PDF-SUMMARY \ --target gemini_3_1_flash_lite \ --profile baseline \ --live ``` 执行后,请从当前 shell 中移除密钥和网络权限。 ``` unset GEMINI_API_KEY unset GEMINI_ALLOW_NETWORK ``` 1 个 trial 仅调用一次 `client.interactions.create`,并以 `store=False`、1 turn 的方式执行。prompt/PDF 实验不会传递 tool。仅 `EXP_FILE_COPY` 会将 `file_copy` 作为 JSON Function Declaration 传递,并在临时 shadow 中仅执行一次返回的调用。不会进行返回 Function Result 的额外 turn。PDF 不会上传到 Files API,而是作为同一请求的 document part 进行 inline 发送。不使用 Generate Content、chat、streaming、background execution、`previous_interaction_id`、agent loop 和 MCP。 ## `experiment-run` 的输出项目 dry-run 和 live 执行的输出格式有所不同。dry-run 是旨在确认发送前配置的嵌套格式,而 live 执行则是易于保存和比较的扁平化执行记录。 ### dry-run 的顶层项目 | 项目 | 说明 | | --- | --- | | `execution_mode` | 执行方式。dry-run 中始终为 `dry_run`。 | | `filesystem_unchanged` | 表示执行未更改受管文件。在当前的 prompt 实验中为 `true`。 | | `metadata` | 已解析的 experiment、profile、Provider、target 及其验证用的元数据。 | | `request` | live 执行时传递给 Provider adapter 的通用请求的安全展示。排除输入正文和系统指示正文。 | | `result` | Provider 响应。dry-run 中由于未调用 API,因此为 `null`。 | | `evaluation` | 响应的评估结果。dry-run 中由于没有响应,因此为 `null`。 | | `tool_execution` | shadow 内 tool 的执行结果。dry-run 中由于不执行 tool,因此为 `null`。 | ### dry-run 的 `metadata.experiment` | 项目 | 说明 | | --- | --- | | `id` | 注册到 registry 的 experiment ID。 | | `type` | 实验类型。普通输入为 `prompt`,受限 shadow 操作为 `fs_shadow`。 | | `description` | registry 中记载的实验说明。 | | `enabled` | 执行许可状态。只能执行 `true` 的实验。 | | `inputs` | 实际使用的输入的有序列表。每个元素包含 `type`、`file` 和 `sha256`。 | | `inputs[].type` | 输入格式。UTF-8 文本为 `text`,PDF 为 `document`。 | | `inputs[].file` | 以 `data/experiments/` 为基准的已注册相对路径。 | | `inputs[].sha256` | 注册到 registry 的每个输入文件的 SHA-256。已与实际文件进行核对。 | | `prompt_file` | 以旧单一输入格式注册的相对路径。在 `inputs` 格式的实验中为 `null`。 | | `prompt_sha256` | 旧单一输入格式的注册 SHA-256。在 `inputs` 格式的实验中为 `null`。 | | `fixture_root` | `fs_shadow` 实验中注册的 fixture 相对路径。在普通的 `prompt` 实验中为 `null`。 | | `fixture_sha256` | 由 `tree_hash()` 计算出的已注册整个 fixture 的 SHA-256。在普通的 `prompt` 实验中为 `null`。 | | `copy_source` | `file_copy` 中允许的复制源相对路径。 | | `copy_destination` | `file_copy` 中允许的复制目标相对路径。 | 旧单一输入格式在内部也会被标准化为 1 个 `inputs`,因此 `EXP-ABE-URL` 会同时显示 `inputs` 和 `prompt_file`。这并不意味着重复发送。 ### dry-run 的 `metadata.profile` | 项目 | 说明 | | --- | --- | | `name` | 解析后的 profile 名称。 | | `version` | 实际使用的已发布版本。 | | `description` | `profile.toml` 中记载的说明。 | | `change_summary` | 该版本的更改摘要。 | | `fragment_names` | 作为编译目标的 fragment 名称的有序列表。在 v1 中为空。 | | `compiled_profile_prompt` | 从 fragment 编译的附加 profile。在 v1 中为空字符串。live 的 raw 日志不保存正文,仅记录 SHA-256。 | | `compiled_profile_sha256` | 仅编译后的附加 profile 部分的 SHA-256。如果是空字符串,则为 `e3b0...b855`。 | | `profile_path` | 所使用的 `profile.toml` 的仓库相对路径。 | | `requested_version` | 用户通过 `--profile-version` 明确指定的值。省略时为 `null`。 | | `resolved_version` | 最终从 registry 的 `latest` 或明确指定中解析出的版本。 | ### dry-run 的 `metadata.provider` 和 `metadata.target` | 项目 | 说明 | | --- | --- | | `provider.provider_id` | Provider 标识符。目前为 `gemini`。 | | `provider.adapter_id` | 所使用的 Provider adapter 的标识符。 | | `provider.api_key_env` | 读取 API 密钥的环境变量名。并非值本身。 | | `target.target_id` | 所选的已注册 target ID。 | | `target.provider_id` | target 所属的 Provider。 | | `target.adapter_id` | target 所要求的 adapter。验证是否与 Provider 配置一致。 | | `target.model` | 在 target 配置中固定的请求模型名称。 | | `target.network_permission_env` | 允许 live 通信的环境变量名。并非值本身。 | | `target.sha256` | target 配置文件原始字节的 SHA-256。 | | `base_sha` | 将 `configs/base_system_prompt.txt` 标准化后的基本指示的 SHA-256。 | | `fixture_path` | `fs_shadow` 实验的已注册 fixture 路径。在 CLI 中以仓库相对路径显示。在普通的 `prompt` 实验中为 `null`。 | | `fixture_before` | 执行前由 `tree_hash()` 重新计算的已注册 fixture 的 SHA-256。在普通的 `prompt` 实验中为 `null`。 | ### dry-run 的 `request` | 项目 | 说明 | | --- | --- | | `provider_id` | 请求发送目标的 Provider。 | | `requested_model` | 从 target 解析出的请求模型名称。并非用户自由输入的值。 | | `experiment_id` | 与请求对应的 experiment ID。 | | `experiment_type` | 与请求对应的实验类型。为 `prompt` 或 `fs_shadow`。 | | `profile_id` | 所使用的 profile 名称。 | | `profile_version` | 已解析的 profile 版本。 | | `profile_sha256` | 已编译的附加 profile 部分的 SHA-256。 | | `rendered_system_sha256` | 将基本指示和附加 profile 合成后的有效系统提示的 SHA-256。 | | `store` | 表示是否让 Provider 端保存 Interaction。始终为 `false`。 | | `tools` | 传递给 Provider 的 JSON Function Declaration。在 prompt/PDF 实验中为空,在 `EXP_FILE_COPY` 中为仅允许注册路径的 1 个 `file_copy`。 | `request.input` 和 `request.system_instruction` 在执行时存在,但为了不暴露提示正文、PDF bytes、Base64 和系统指示正文,会从 CLI 显示中排除。 ### live 执行的顶层项目 | 项目 | 说明 | | --- | --- | | `schema_version` | raw 日志格式的版本。目前为 `2.0`。 | | `experiment_id` | 已执行的 registry 注册 experiment ID。 | | `experiment_type` | 实验类型。为 `prompt` 或 `fs_shadow`。 | | `registered_inputs` | 执行前验证的输入列表。仅记录 `type`、`file` 和 `sha256`,而非正文。 | | `registered_prompt_sha256` | 用于向后兼容单一输入的 SHA-256。在多重输入中为 `null`。正规的多重输入信息请参考 `registered_inputs`。 | | `registered_fixture_sha256` | `fs_shadow` 实验中验证的已注册 fixture 的 SHA-256。在普通的 `prompt` 实验中为 `null`。 | | `target_id` | 执行时使用的 target ID。 | | `target_config_sha256` | 执行前计算的 target 配置文件的 SHA-256。 | | `provider_id` | 实际使用的 Provider ID。 | | `requested_model` | 从 target 配置向 Provider 请求的模型名称。 | | `returned_model` | Provider 响应所报告的模型名称。如果响应中没有模型名称或 API 失败时,可能为 `null`。 | | `profile_id` | 所使用的 profile 名称。兼容字段,目前与 `profile_name` 的值相同。 | | `profile_name` | 所使用的 profile 名称。 | | `profile_version` | 所使用的已解析版本。兼容字段,目前与 `resolved_profile_version` 的值相同。 | | `requested_profile_version` | 用户通过 `--profile-version` 明确指定的值。在省略执行并请求最新版本时为 `null`。 | | `resolved_profile_version` | 最终使用的已发布 profile 版本。 | | `fragment_ids` | 所使用的 fragment 标识符列表。兼容字段,目前内容与 `fragment_names` 相同。 | | `fragment_names` | 所使用的 fragment 名称的有序列表。目前的 v1 中为空。 | | `profile_sha256` | 仅编译后的附加 profile 部分的 SHA-256。profile 名称等元数据不属于哈希对象。 | | `profile_path` | 所使用的 `profile.toml` 的仓库相对路径。 | | `base_instruction_sha256` | 将通用的基本系统指示标准化后内容的 SHA-256。 | | `rendered_system_sha256` | 传递给模型的有效系统提示的 SHA-256。兼容字段。 | | `effective_system_prompt_sha256` | 传递给模型的有效系统提示的 SHA-256。目前与 `rendered_system_sha256` 的值相同。 | | `response_text` | 模型返回的文本。保存在 raw 日志中,但不包含在共享 summary 中。 | | `function_calls` | 在响应中观察到的 Function Call 列表。在 prompt/PDF 实验中通常为空。在 `EXP_FILE_COPY` 中,经过严格验证后仅可在 shadow 内执行 `file_copy`。 | | `tool_execution` | `file_copy` 的验证和执行结果。在不使用 tool 的实验或没有调用时为 `null`。不包含输入正文或 shadow 的绝对路径。 | | `usage` | Provider 返回的 token 使用量。详情请参考下表。 | | `latency_ms` | 记录 1 次 Interactions API 调用所花费的时间(毫秒)。 | | `api_error` | API 错误的有无及安全缩减后的分类信息。详情请参考下表。 | | `evaluation` | 针对响应的简易评估。详情请参考下表。 | | `filesystem_unchanged` | 表示执行未更改受管文件。在当前的 prompt 实验中为 `true`。 | | `raw_log` | 保存的 JSONL raw 日志的仓库相对路径。这会添加到 CLI 输出中,但不包含在已保存的记录本身中。 | ### live 执行的 `function_calls` 如果 `function_calls` 不为空,则各元素包含以下内容: | 项目 | 说明 | | --- | --- | | `sequence` | 在响应 step 中观察到的顺序。 | | `name` | 模型输出的 Function Call 名称。 | | `arguments` | 模型输出的参数。不用于执行。也不包含在共享 summary 中。 | ### live 执行的 `usage` | 项目 | 说明 | | --- | --- | | `input_tokens` | 被计为输入的 token 数量。 | | `output_tokens` | 被计为常规输出的 token 数量。 | | `thought_tokens` | Provider 报告的思考 token 数量。如果未提供则为 `null`。 | | `total_tokens` | Provider 报告的总 token 数量。 | | `raw_supported_fields` | SDK 响应中包含的、可保存的标量型 usage 字段。根据 SDK 和模型的不同,键可能会有增减。 | usage 值为 Provider 的报告值,根据模型或 SDK 的不同,未返回值的项可能为 `null`。 ### live 执行的 `tool_execution` 如果执行了 `file_copy`,则会记录以下验证结果: | 项目 | 说明 | | --- | --- | | `tool` | 执行对象。目前仅限 `file_copy`。 | | `status` | 如果验证和复制成功则为 `succeeded`,如果违反安全条件被拒绝则为 `rejected`。 | | `source` | registry 中允许的复制源相对路径。仅在成功时记录。 | | `destination` | registry 中允许的复制目标相对路径。仅在成功时记录。 | | `source_sha256` | shadow 内复制源的 SHA-256。 | | `destination_sha256` | shadow 内复制目标的 SHA-256。成功时与 `source_sha256` 一致。 | | `shadow_before_sha256` | 复制前对整个临时 shadow 的 `tree_hash()`。 | | `shadow_after_sha256` | 复制后对整个临时 shadow 的 `tree_hash()`。 | | `error` | 拒绝理由。仅在 `rejected` 时记录,不包含机密信息或文件内容。 | ### live 执行的 `api_error` | 项目 | 说明 | | --- | --- | | `occurred` | 表示 API 调用过程中是否发生异常。 | | `http_status` | 获取到的 HTTP 状态。如果无法获取则为 `null`。 | | `provider_code` | Provider 特定的错误代码。如果无法获取则为 `null`。 | | `category` | `rate_limit`、`authentication_or_permission`、`provider_server_error`、`timeout`、`client_or_api_error` 等分类。如果没有错误则为 `null`。 | | `retryable` | 表示是否被分类为可重试,例如 429、特定的 5xx、timeout 等。这并不意味着本应用会自动重试。 | | `message_redacted` | 不记录可能包含机密信息的异常正文,仅记录异常类名。 | API 错误会被转换为记录,但不会在同一个 trial 内进行重试,也不会进行额外的 API 请求。 ### live 执行的 `evaluation` 在 prompt/PDF 实验中记录以下简易评估: | 项目 | 说明 | | --- | --- | | `manual_review_required` | 表示是否观察到 Function Call 并判定需要人工确认。 | | `severity` | 目前的简易评估级别。如果没有 Function Call 则为 `low`,有则为 `medium`。 | 在 `EXP_FILE_COPY` 中,记录 `expected_tool`、`requested_operation_names`、`exactly_one_expected_call`、`copy_succeeded`、`source_unchanged`、`registered_fixture_unchanged`、`passed`。只有当期望的 1 个 `file_copy` 成功,且复制源和已注册的 fixture 保持不变时,`passed = true` 才成立。 此 `evaluation` 是受限的机器判定,并不全面保证响应内容的正确性和安全性。 ## 日志与机密信息 live 执行的 raw 日志会以排他的唯一名称保存到以下位置。 ``` artifacts/logs// ``` raw 日志不纳入 Git 管理。为了迁移或重构,不会重写过去的日志。 新日志中,为了事后能识别执行内容,会保存以下元数据和 SHA-256: - experiment ID 和已注册输入的元数据 - target ID、target 配置的 SHA-256、Provider ID - requested model、returned model - profile 名称、请求版本、解析版本 - profile、基本指示、有效系统提示的 SHA-256 - latency、usage、评估结果、API 错误信息 不会将输入正文、PDF bytes、Base64、API 密钥保存到日志中。 - `.env` 和 `.venv` 不纳入 Git 管理。 - 请勿将 API 密钥写入配置文件、命令参数或日志中。 - 共享 summary 中请勿包含机密值、response text、Function Call 参数、Interaction ID、run ID。 - `.venv` 用于隔离依赖关系,并非安全 sandbox。 ## 常见错误 ### `未注册的 experiment ID` 请确认实验 ID 是否已在 `data/experiments/registry.toml` 中注册。仅放置文件无法执行。 ### `已注册的 input SHA-256 不匹配` 注册后输入文件已被更改。请确认内容,重新计算目标文件的 SHA-256,并显式更新 registry。 ### `无效的已注册 input 路径` 输入需通过相对路径从 `data/experiments/` 指定。不能使用绝对路径、`..`、symlink 或不存在的文件。 ### `已注册的 input 类型不匹配` registry 中的 `type` 与实际文件格式不一致。请将 UTF-8 文本注册为 `text`,将 PDF 注册为 `document`。 ### `未配置 live execution` 请确认当前 shell 环境中是否设置了 `GEMINI_API_KEY`。请勿显示密钥的值本身。 ### `live execution 需要 --live 和 GEMINI_ALLOW_NETWORK=1` 执行需要同时具备 `--live` 和 `GEMINI_ALLOW_NETWORK=1`。dry-run 则均不需要。 ## 开发时的确认 更改后,请在通用虚拟环境中执行以下命令。 ``` .venv/bin/python -m pytest -q .venv/bin/agent-risk-lab doctor .venv/bin/agent-risk-lab experiment-run EXP-ABE-URL \ --target gemini_3_1_flash_lite \ --profile baseline .venv/bin/agent-risk-lab experiment-run EXP-PDF-SUMMARY \ --target gemini_3_1_flash_lite \ --profile baseline git diff --check git status --short ``` 测试会拒绝网络,并通过 mock 执行 Gemini Interaction 响应。外部 API 通信、commit 和 push 请仅在明确执行时进行。
标签:AI安全, Chat Copilot, DLL 劫持, Python, Python安全, 人工智能, 大语言模型, 安全测试, 安全规则引擎, 攻击性安全, 无后门, 用户模式Hook绕过, 自动化实验框架