{"meta":{"title":"将挂钩与 Copilot CLI 配合使用，实现可预测且符合策略的执行","intro":"使用挂钩记录用户提示并控制哪些工具 Copilot CLI 可以在存储库中运行，以便团队可以在组织的安全性和合规性要求内安全地自动执行。","product":"GitHub Copilot","breadcrumbs":[{"href":"/zh/copilot","title":"GitHub Copilot"},{"href":"/zh/copilot/tutorials","title":"教程"},{"href":"/zh/copilot/tutorials/copilot-cli-hooks","title":"在 Copilot CLI 中使用钩子功能"}],"documentType":"article"},"body":"# 将挂钩与 Copilot CLI 配合使用，实现可预测且符合策略的执行\n\n使用挂钩记录用户提示并控制哪些工具 Copilot CLI 可以在存储库中运行，以便团队可以在组织的安全性和合规性要求内安全地自动执行。\n\n本教程面向 DevOps 工程师、平台团队和工程主管，他们支持使用 Copilot CLI 的开发人员。\n\n挂钩是在 Copilot CLI 会话期间特定时间点运行的自定义脚本。 他们可以检查提示和工具调用、记录审核信息，甚至阻止执行某些命令。\n\n你将配置一些作用于存储库范围的钩子：\n\n* 提供对提示和工具使用情况的可见性。\n* 在执行前阻止高风险命令模式。\n* 帮助开发人员了解具有明确消息传送的组织策略。\n\n## 先决条件\n\n* 熟悉 shell 脚本（Bash 或 PowerShell）。\n* 基本了解 JSON 配置文件。\n* 对使用了 Copilot CLI 的存储库的访问权限。\n* 对于 Bash 示例： `jq` 必须安装。\n* 对于 PowerShell 示例：必须安装 PowerShell 7.0 或更高版本。\n\n## 1. 定义组织策略\n\n在编写任何挂钩脚本之前，请确定哪些操作应自动允许，哪些操作需要人工审核。\n\n明确的策略有助于避免过度阻塞，同时仍降低风险。\n\n### 确定始终需要审阅的命令\n\n首先标识不应由Copilot CLI自动执行的模式。 常见示例包括：\n\n* **权限提升**： `sudo`， ， `su``runas`\n* **破坏性系统操作**：`rm -rf /`、`mkfs`、`dd`、`format`\n* **下载和执行模式**：`curl ... | bash`、PowerShell `wget ... | sh``iex (irm ...)`\n\n这些命令在无意中执行时可能会产生不可逆的影响。\n\n### 确定要记录的内容\n\n使用挂钩时，你可捕获 Copilot CLI 在存储库中的使用信息，包括用户提交的提示和 Copilot CLI 尝试运行的工具。\n\n大多数组织至少记录：\n\n* 时间戳和存储库路径\n* 提示文本（或经过编辑的形式）\n* 工具名称和工具参数\n* 任何策略决策（例如，被拒绝的命令及其原因）\n\n请勿在日志中记录机密信息或凭证。 如果提示或命令可能包含敏感数据，请在编写日志之前应用修订。\n\n本教程使用本地 `.github/hooks/logs` 目录作为一个简单的演示示例。 这些日志文件 **不打算提交到存储库** ，通常仅存在于开发人员的计算机上。\n\n在生产环境中，许多组织将事件转发到集中式日志记录或可观测性系统，而不是在本地编写日志。 这样，团队就可以在存储库和用户之间应用一致的编辑、访问控制、保留策略和监控。\n\n### 与利益干系人保持一致\n\n在强制实施策略之前，与以下人员一同审核它们：\n\n* 安全或合规性团队，以确认风险边界\n* 平台或基础结构团队，他们可能需要更广泛的权限\n* 开发团队，因此他们了解将被阻止的内容以及原因\n\n明确的预期使政策强制实施更容易采用和维护。\n\n## 2.设置存储库挂钩文件\n\n在整个教程中，你将使用存储在仓库中 \\*\\*\\*\\* 下的`.github/hooks/`。 每当 Copilot CLI 从该存储库运行时，这些挂钩都会适用。\n\n> \\[!NOTE]\n> Copilot 代理从 `.github/hooks/*.json` 存储库中的文件加载挂钩配置。 钩子同步运行，可以阻止程序的执行。\n\n### 创建目录结构\n\n在存储库根目录中，为挂钩配置、脚本和日志创建目录：\n\n```bash copy\nmkdir -p .github/hooks/scripts\nmkdir -p .github/hooks/logs\n```\n\n将 `.github/hooks/logs/` 添加到 .gitignore 中，以避免本地审计日志被提交：\n\n```bash copy\necho \".github/hooks/logs/\" >> .gitignore\n```\n\n本教程使用以下结构：\n\n```text\n.github/\n└── hooks/\n    ├── copilot-cli-policy.json\n    ├── logs/\n    │   └── audit.jsonl\n    └── scripts/\n        ├── session-banner.sh\n        ├── session-banner.ps1\n        ├── log-prompt.sh\n        ├── log-prompt.ps1\n        ├── pre-tool-policy.sh\n        └── pre-tool-policy.ps1\n```\n\n> \\[!NOTE]\n> 本教程旨在创建可在 Windows、Linux 和 macOS 上使用的可移植挂钩配置和脚本。 因此，该 `scripts` 目录将同时包含 Bash 和 PowerShell 脚本，挂钩配置文件将包括 `bash` 和 `powershell` 条目。 CLI 将根据操作系统使用相应的条目。\n\n### 创建挂钩配置文件\n\n在 `.github/hooks/copilot-cli-policy.json` 创建挂钩配置文件。\n\n此文件定义哪些挂钩、何时运行以及执行哪些脚本。\n\n```json copy\n{\n  \"version\": 1,\n  \"hooks\": {\n    \"sessionStart\": [\n      {\n        \"type\": \"command\",\n        \"bash\": \"./scripts/session-banner.sh\",\n        \"powershell\": \"./scripts/session-banner.ps1\",\n        \"cwd\": \".github/hooks\",\n        \"timeoutSec\": 10\n      }\n    ],\n    \"userPromptSubmitted\": [\n      {\n        \"type\": \"command\",\n        \"bash\": \"./scripts/log-prompt.sh\",\n        \"powershell\": \"./scripts/log-prompt.ps1\",\n        \"cwd\": \".github/hooks\",\n        \"timeoutSec\": 10\n      }\n    ],\n    \"preToolUse\": [\n      {\n        \"type\": \"command\",\n        \"bash\": \"./scripts/pre-tool-policy.sh\",\n        \"powershell\": \"./scripts/pre-tool-policy.ps1\",\n        \"cwd\": \".github/hooks\",\n        \"timeoutSec\": 15\n      }\n    ]\n  }\n}\n```\n\n### 了解此配置的作用\n\n此配置设置三个挂钩：\n\n* `sessionStart`：显示新代理会话启动或恢复时的信息性消息。\n* `userPromptSubmitted`：每当用户提交提示时运行。\n* `preToolUse`：在工具执行之前运行，并且可以显式允许或拒绝执行。\n\n### 提交并共享挂钩配置\n\n当您准备好与协作者共享钩子配置时（例如，通过拉取请求或在测试存储库中），请提交钩子配置和脚本。 不要提交任何本地审核日志。\n\n```bash copy\ngit add .github/hooks/copilot-cli-policy.json .github/hooks/scripts\ngit commit -m \"Add Copilot CLI hook configuration\"\ngit push\n```\n\n此时，Copilot CLI 可发现你的挂钩配置，即使你尚未创建挂钩脚本。\n\n## 3. 在会话开始时添加策略横幅\n\n使用 `sessionStart` 挂钩在新 Copilot CLI 会话启动或恢复时显示横幅。 这向开发人员明确表示组织策略处于活动状态。\n\n挂钩函数 `sessionStart` 接收上下文相关信息，例如当前工作目录和初始提示。 此挂钩的任何输出将被 Copilot CLI忽略，这使得它适合信息性消息。\n\n### 创建会话横幅脚本 （Bash）\n\n创建 `.github/hooks/scripts/session-banner.sh`：\n\n```bash copy\n#!/bin/bash\nset -euo pipefail\n\ncat << 'EOF'\nCOPILOT CLI POLICY ACTIVE\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Prompts and tool use may be logged for auditing\n• High-risk commands may be blocked automatically\n• If something is blocked, follow the guidance shown\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nEOF\nexit 0\n```\n\n### 创建会话横幅脚本 （PowerShell）\n\n创建 `.github/hooks/scripts/session-banner.ps1`：\n\n```powershell copy\n$ErrorActionPreference = \"Stop\"\n\nWrite-Host @\"\nCOPILOT CLI POLICY ACTIVE\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Prompts and tool use may be logged for auditing\n• High-risk commands may be blocked automatically\n• If something is blocked, follow the guidance shown\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\"@\nexit 0\n```\n\n### 测试会话横幅\n\n可以直接测试横幅脚本：\n\n```bash\n.github/hooks/scripts/session-banner.sh\n# or, for PowerShell\n.github/hooks/scripts/session-banner.ps1\n```\n\n运行任一脚本时，应会看到终端中显示的策略横幅。\n\n## 4.记录提示以用于审计\n\n当用户提交提示到`userPromptSubmitted`时，使用Copilot CLI挂钩进行记录。 每次发送提示时，都会先运行此钩子，然后再调用任何工具。\n\n挂钩接收结构化 JSON 输入，其中包括时间戳、当前工作目录和完整提示文本。 忽略此钩子的输出。\n\n> \\[!IMPORTANT]\n> 提示可能包含敏感信息。 在记录此数据时应用修订并遵循组织的数据处理和保留策略。\n\n### 创建命令提示符日志记录脚本 （Bash）\n\n创建 `.github/hooks/scripts/log-prompt.sh`：\n\n```bash copy\n#!/bin/bash\nset -euo pipefail\n\nINPUT=\"$(cat)\"\n\nTIMESTAMP_MS=\"$(echo \"$INPUT\" | jq -r '.timestamp // empty')\"\nCWD=\"$(echo \"$INPUT\" | jq -r '.cwd // empty')\"\n\n# This example logs only metadata, not the full prompt, to avoid storing\n# potentially sensitive data. Adjust to match your organization’s needs.\nLOG_DIR=\".github/hooks/logs\"\nmkdir -p \"$LOG_DIR\"\nchmod 700 \"$LOG_DIR\"\n\njq -n \\\n  --arg ts \"$TIMESTAMP_MS\" \\\n  --arg cwd \"$CWD\" \\\n  '{event:\"userPromptSubmitted\", timestampMs:$ts, cwd:$cwd}' \\\n  >> \"$LOG_DIR/audit.jsonl\"\n\nexit 0\n```\n\n### 创建提示日志记录脚本 （PowerShell）\n\n创建 `.github/hooks/scripts/log-prompt.ps1`：\n\n```powershell copy\n$ErrorActionPreference = \"Stop\"\n\n$inputObj = [Console]::In.ReadToEnd() | ConvertFrom-Json\n\n$timestampMs = $inputObj.timestamp\n$cwd = $inputObj.cwd\n$prompt = $inputObj.prompt\n\n# Optional example redaction. Adjust to match your organization’s needs.\n$redactedPrompt = $prompt -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]'\n\n$logDir = \".github/hooks/logs\"\nif (-not (Test-Path $logDir)) {\n  New-Item -ItemType Directory -Path $logDir -Force | Out-Null\n}\n\n$logEntry = @{\n  event       = \"userPromptSubmitted\"\n  timestampMs = $timestampMs\n  cwd         = $cwd\n  prompt      = $redactedPrompt\n} | ConvertTo-Json -Compress\n\nAdd-Content -Path \"$logDir/audit.jsonl\" -Value $logEntry\nexit 0\n```\n\n### 测试提示日志记录脚本\n\n你可以通过管道传入示例输入来直接测试脚本。\n\n```bash\necho '{\"timestamp\":1704614500000,\"cwd\":\"/repo\",\"prompt\":\"List all branches\"}' \\\n  | .github/hooks/scripts/log-prompt.sh\n# or, for PowerShell\necho '{\"timestamp\":1704614500000,\"cwd\":\"/repo\",\"prompt\":\"List all branches\"}' |\n  .github/hooks/scripts/log-prompt.ps1\n```\n\n运行脚本后，检查`.github/hooks/logs/audit.jsonl`是否有新的日志条目。\n\n```bash copy\ncat .github/hooks/logs/audit.jsonl\n```\n\n在此阶段，提交至 Copilot CLI 此存储库的提示会被记录以供审核。\n\n## 5.使用 `preToolUse` 强制执行策略\n\n使用`preToolUse`挂钩来在**工具调用运行之前**进行评估。 此挂钩可以允许执行（不采取任何行动）或拒绝执行（通过返回结构化响应）。\n\n### 了解 `preToolUse` 的输入\n\n`preToolUse` hook 的输入包括：\n\n* `toolName`：即将运行的工具Copilot CLI（例如） `bash`\n* `toolArgs`：包含该工具参数的 **JSON 字符串**\n\n因为 `toolArgs` 是 JSON 字符串，所以脚本在读取类似 `command`字段之前必须对其进行分析。\n\n> \\[!IMPORTANT]\n> 工具参数和命令可能包含敏感信息，例如 API 令牌、密码或其他凭据。 在记录此数据之前应用修订，并遵循组织的安全策略。 请考虑仅记录非敏感元数据（工具名称、时间戳、策略决策），并将审核事件定向到具有适当access控制和保留策略的安全集中日志记录系统。\n\n### 创建策略脚本\n\n接下来，创建策略脚本。 本示例：\n\n* 记录所有尝试的工具使用情况。\n* 仅对 bash 命令应用拒绝规则。\n* 阻止高风险模式，例如特权升级、破坏性操作和下载并执行命令。\n\n为了安全地验证拒绝流，该脚本还包括一个临时演示规则，用于阻止无害的测试命令。 在确认 hooks 按预期工作后，移除演示规则，并将其替换为符合你组织策略的模式。\n\n#### 示例脚本 （Bash）\n\n创建 `.github/hooks/scripts/pre-tool-policy.sh`：\n\n```bash copy\n#!/bin/bash\nset -euo pipefail\n\nINPUT=\"$(cat)\"\n\nTOOL_NAME=\"$(echo \"$INPUT\" | jq -r '.toolName // empty')\"\nTOOL_ARGS_RAW=\"$(echo \"$INPUT\" | jq -r '.toolArgs // empty')\"  # JSON string\n\nLOG_DIR=\".github/hooks/logs\"\nmkdir -p \"$LOG_DIR\"\n\n# Example redaction logic.\n# GitHub does not currently provide built-in secret redaction for hooks.\n# This example shows one possible approach; many organizations prefer to\n# forward events to a centralized logging system that handles redaction.\n# Redact sensitive patterns before logging.\n# Adjust these patterns to match your organization's needs.\nREDACTED_TOOL_ARGS=\"$(echo \"$TOOL_ARGS_RAW\" | \\\n  sed -E 's/ghp_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/gho_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/ghu_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/ghs_[A-Za-z0-9\\._\\-]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/Bearer [A-Za-z0-9_\\-\\.]+/Bearer [REDACTED]/g' | \\\n  sed -E 's/--password[= ][^ ]+/--password=[REDACTED]/g' | \\\n  sed -E 's/--token[= ][^ ]+/--token=[REDACTED]/g')\"\n\n# Log attempted tool use with redacted toolArgs.\njq -n \\\n  --arg tool \"$TOOL_NAME\" \\\n  --arg toolArgs \"$REDACTED_TOOL_ARGS\" \\\n  '{event:\"preToolUse\", toolName:$tool, toolArgs:$toolArgs}' \\\n  >> \"$LOG_DIR/audit.jsonl\"\n\n# Only enforce command rules for bash.\nif [ \"$TOOL_NAME\" != \"bash\" ]; then\n  exit 0\nfi\n\n# Parse toolArgs JSON string.\n# If toolArgs isn't valid JSON for some reason, allow (and rely on logs).\nif ! echo \"$TOOL_ARGS_RAW\" | jq -e . >/dev/null 2>&1; then\n  exit 0\nfi\n\nCOMMAND=\"$(echo \"$TOOL_ARGS_RAW\" | jq -r '.command // empty')\"\n\n# ---------------------------------------------------------------------------\n# Demo-only deny rule for safe testing.\n# This blocks a harmless test command so you can validate the deny flow.\n# Remove this rule after confirming your hooks work as expected.\n# ---------------------------------------------------------------------------\nif echo \"$COMMAND\" | grep -q \"COPILOT_HOOKS_DENY_DEMO\"; then\n  deny \"Blocked demo command (test rule). Remove this rule after validating hooks.\"\nfi\n\ndeny() {\n  local reason=\"$1\"\n\n  # Redact sensitive patterns from command before logging.\n  local redacted_cmd=\"$(echo \"$COMMAND\" | \\\n    sed -E 's/ghp_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/gho_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/ghu_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/ghs_[A-Za-z0-9\\.\\-_]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/Bearer [A-Za-z0-9_\\-\\.]+/Bearer [REDACTED]/g' | \\\n    sed -E 's/--password[= ][^ ]+/--password=[REDACTED]/g' | \\\n    sed -E 's/--token[= ][^ ]+/--token=[REDACTED]/g')\"\n\n  # Log the denial decision with redacted command.\n  jq -n \\\n    --arg cmd \"$redacted_cmd\" \\\n    --arg r \"$reason\" \\\n    '{event:\"policyDeny\", toolName:\"bash\", command:$cmd, reason:$r}' \\\n    >> \"$LOG_DIR/audit.jsonl\"\n\n  # Return a denial response.\n  jq -n \\\n    --arg r \"$reason\" \\\n    '{permissionDecision:\"deny\", permissionDecisionReason:$r}'\n\n  exit 0\n}\n\n# Privilege escalation\nif echo \"$COMMAND\" | grep -qE '\\b(sudo|su|runas)\\b'; then\n  deny \"Privilege escalation requires manual approval.\"\nfi\n\n# Destructive filesystem operations targeting root\nif echo \"$COMMAND\" | grep -qE 'rm\\s+-rf\\s*/($|\\s)|rm\\s+.*-rf\\s*/($|\\s)'; then\n  deny \"Destructive operations targeting the filesystem root require manual approval.\"\nfi\n\n# System-level destructive operations\nif echo \"$COMMAND\" | grep -qE '\\b(mkfs|dd|format)\\b'; then\n  deny \"System-level destructive operations are not allowed via automated execution.\"\nfi\n\n# Download-and-execute patterns\nif echo \"$COMMAND\" | grep -qE 'curl.*\\|\\s*(bash|sh)|wget.*\\|\\s*(bash|sh)'; then\n  deny \"Download-and-execute patterns require manual approval.\"\nfi\n\n# Allow by default\nexit 0\n```\n\n#### 创建策略脚本 （PowerShell）\n\n创建 `.github/hooks/scripts/pre-tool-policy.ps1`：\n\n```powershell copy\n$ErrorActionPreference = \"Stop\"\n\n$inputObj = [Console]::In.ReadToEnd() | ConvertFrom-Json\n$toolName = $inputObj.toolName\n$toolArgsRaw = $inputObj.toolArgs  # JSON string\n\n$logDir = \".github/hooks/logs\"\nif (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }\n\n# Example redaction logic.\n# GitHub does not currently provide built-in secret redaction for hooks.\n# This example shows one possible approach; many organizations prefer to\n# forward events to a centralized logging system that handles redaction.\n# Redact sensitive patterns before logging.\n# Adjust these patterns to match your organization's needs.\n$redactedToolArgs = $toolArgsRaw `\n  -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'gho_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'ghu_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'ghs_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'Bearer [A-Za-z0-9_\\-\\.]+', 'Bearer [REDACTED]' `\n  -replace '--password[= ][^ ]+', '--password=[REDACTED]' `\n  -replace '--token[= ][^ ]+', '--token=[REDACTED]'\n\n# Log attempted tool use with redacted toolArgs.\n(@{\n  event    = \"preToolUse\"\n  toolName = $toolName\n  toolArgs = $redactedToolArgs\n} | ConvertTo-Json -Compress) | Add-Content -Path \"$logDir/audit.jsonl\"\n\nif ($toolName -ne \"bash\") { exit 0 }\n\n# Parse toolArgs JSON string.\n$toolArgs = $null\ntry { $toolArgs = $toolArgsRaw | ConvertFrom-Json } catch { exit 0 }\n\n$command = $toolArgs.command\n\n# ---------------------------------------------------------------------------\n# Demo-only deny rule for safe testing.\n# This blocks a harmless test command so you can validate the deny flow.\n# Remove this rule after confirming your hooks work as expected.\n# ---------------------------------------------------------------------------\nif ($command -match 'COPILOT_HOOKS_DENY_DEMO') {\n  Deny \"Blocked demo command (test rule). Remove this rule after validating hooks.\"\n}\n\nfunction Deny([string]$reason) {\n  # Redact sensitive patterns from command before logging.\n  $redactedCommand = $command `\n    -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'gho_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'ghu_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'ghs_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'Bearer [A-Za-z0-9_\\-\\.]+', 'Bearer [REDACTED]' `\n    -replace '--password[= ][^ ]+', '--password=[REDACTED]' `\n    -replace '--token[= ][^ ]+', '--token=[REDACTED]'\n\n  # Log the denial decision with redacted command.\n  (@{\n    event    = \"policyDeny\"\n    toolName = \"bash\"\n    command  = $redactedCommand\n    reason   = $reason\n  } | ConvertTo-Json -Compress) | Add-Content -Path \"$logDir/audit.jsonl\"\n\n  (@{\n    permissionDecision = \"deny\"\n    permissionDecisionReason = $reason\n  } | ConvertTo-Json -Compress)\n\n  exit 0\n}\n\nif ($command -match '\\b(sudo|su|runas)\\b') { Deny \"Privilege escalation requires manual approval.\" }\nif ($command -match 'rm\\s+-rf\\s*/(\\s|$)|rm\\s+.*-rf\\s*/(\\s|$)') { Deny \"Destructive operations targeting the filesystem root require manual approval.\" }\nif ($command -match '\\b(mkfs|dd|format)\\b') { Deny \"System-level destructive operations are not allowed via automated execution.\" }\nif ($command -match 'curl.*\\|\\s*(bash|sh)|wget.*\\|\\s*(bash|sh)') { Deny \"Download-and-execute patterns require manual approval.\" }\n\nexit 0\n```\n\n### 测试策略脚本\n\n你可以通过管道传入示例 `preToolUse` 输入来测试脚本。\n\n允许示例：\n\n```bash\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"git status\\\"}\"}' \\\n  | .github/hooks/scripts/pre-tool-policy.sh\n# or, for PowerShell\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"git status\\\"}\"}' |\n  .github/hooks/scripts/pre-tool-policy.ps1\n```\n\n拒绝示例：\n\n```bash\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"sudo rm -rf /\\\"}\"}' \\\n  | .github/hooks/scripts/pre-tool-policy.sh\n# or, for PowerShell\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"sudo rm -rf /\\\"}\"}' |\n  .github/hooks/scripts/pre-tool-policy.ps1\n```\n\n运行拒绝示例后，检查`.github/hooks/logs/audit.jsonl`是否有新的拒绝日志条目。\n\n```json\n{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Privilege escalation requires manual approval.\"}\n```\n\n此时，会阻止高风险 `bash` 命令在此存储库中自动执行。\n\n## 6. 在存储库中端到端测试\n\n创建配置文件和脚本后，验证在此存储库中使用 Copilot CLI 时挂钩按预期运行。\n\n### 验证挂钩配置文件\n\n检查挂钩配置文件是否为有效的 JSON：\n\n```bash copy\njq '.' < .github/hooks/copilot-cli-policy.json\n```\n\n### 验证脚本权限（基于 Unix 的系统）\n\n在 macOS 和 Linux 上，确认 Bash 脚本是可执行的：\n\n```bash copy\nchmod +x .github/hooks/scripts/*.sh\n```\n\n### 运行基本会话\n\n在存储库中启动新 Copilot CLI 会话：\n\n```bash copy\ncopilot -p \"Show me the status of this repository\"\n```\n\n预期结果：\n\n* 你将看到策略横幅（来自 `sessionStart`）。\n* 将新条目添加到 `.github/hooks/logs/audit.jsonl`（从 `userPromptSubmitted`）。\n\n### 触发工具的使用与日志记录验证\n\n运行一个提示，以使 Copilot CLI 使用工具（例如 bash）：\n\n```bash copy\ncopilot -p \"Show me the last 5 git commits\"\n```\n\n预期结果：\n\n* `preToolUse` 条目将添加到 `.github/hooks/logs/audit.jsonl`。\n* 如果允许工具调用，则执行将正常进行。\n\n### 测试被拒绝的命令\n\n示例策略脚本包括一个临时演示规则，用于阻止包含字符串 `COPILOT_HOOKS_DENY_DEMO`的命令。 这样就可以安全地验证拒绝流，而无需运行破坏性命令。\n\n运行一个会导致命令被拒绝的提示：\n\n```bash copy\ncopilot -p \"Run a test command: echo COPILOT_HOOKS_DENY_DEMO\"\n```\n\n预期结果：\n\n* Copilot CLI 不执行命令。\n* 钩子函数返回一个拒绝响应，并附有明确的原因。\n* `policyDeny` 条目写入 `.github/hooks/logs/audit.jsonl`。\n\n确认拒绝流正常工作后，请从脚本中删除演示规则，并将其替换为反映组织策略的拒绝模式。\n\n### 检查审核日志\n\n查看最近的条目：\n\n```bash copy\ntail -n 50 .github/hooks/logs/audit.jsonl\n```\n\n仅筛选被拒绝的决定：\n\n```bash copy\njq 'select(.event==\"policyDeny\")' .github/hooks/logs/audit.jsonl\n```\n\n## 7. 跨团队安全部署\n\n在单个存储库中验证钩子后，逐步推出以避免中断开发工作流。\n\n### 选择推出策略\n\n常见的推出方法包括：\n\n* **日志记录优先推出（建议）**：首先记录提示和工具使用情况，而无需拒绝执行。 查看一段时间的日志，然后在了解常见使用模式后引入拒绝规则。\n* **团队部署**：一次将挂钩部署到一个团队或存储库，收集反馈，然后扩展到其他团队。\n* **基于风险的推出**：从处理敏感系统或生产基础结构的存储库开始，然后扩展到风险较低的存储库。\n\n### 传达期望\n\n在强制实施拒绝规则之前，请确保开发人员了解：\n\n* 这表明 hooks 已在仓库中处于活动状态\n* 可以阻止哪些类型的命令\n* 如果执行命令被拒绝，应如何继续操作\n\n清晰的沟通从而减少混淆和支持请求。\n\n### 使策略保持可维护\n\n随着使用情况的发展：\n\n* 将挂钩配置和脚本存储在版本控制中。\n* 定期查看审核日志，以检测新的风险模式。\n* 请逐步更新拒绝规则，而不要一次性添加范围过大的匹配规则。\n* 记录每个拒绝规则存在的原因，尤其是对于高影响限制。\n\n### 小心处理异常\n\n某些团队（例如基础结构或平台团队）可能需要更广泛的权限。 若要安全地处理此问题，\n\n* 维护不同存储库的单独挂钩配置。\n* 确保例外范围较窄且记录完善。\n* 避免临时性的本地绕过，这会削弱审计透明度。\n\n## 延伸阅读\n\n有关 hooks 的故障排查，请参阅 [使用挂钩自定义代理工作流](/zh/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks#troubleshooting)。"}