# 将挂钩与 Copilot CLI 配合使用，实现可预测且符合策略的执行

使用挂钩记录用户提示并控制哪些工具 Copilot CLI 可以在存储库中运行，以便团队可以在组织的安全性和合规性要求内安全地自动执行。

本教程面向 DevOps 工程师、平台团队和工程主管，他们支持使用 Copilot CLI 的开发人员。

挂钩是在 Copilot CLI 会话期间特定时间点运行的自定义脚本。 他们可以检查提示和工具调用、记录审核信息，甚至阻止执行某些命令。

你将配置一些作用于存储库范围的钩子：

* 提供对提示和工具使用情况的可见性。
* 在执行前阻止高风险命令模式。
* 帮助开发人员了解具有明确消息传送的组织策略。

## 先决条件

* 熟悉 shell 脚本（Bash 或 PowerShell）。
* 基本了解 JSON 配置文件。
* 对使用了 Copilot CLI 的存储库的访问权限。
* 对于 Bash 示例： `jq` 必须安装。
* 对于 PowerShell 示例：必须安装 PowerShell 7.0 或更高版本。

## 1. 定义组织策略

在编写任何挂钩脚本之前，请确定哪些操作应自动允许，哪些操作需要人工审核。

明确的策略有助于避免过度阻塞，同时仍降低风险。

### 确定始终需要审阅的命令

首先标识不应由Copilot CLI自动执行的模式。 常见示例包括：

* **权限提升**： `sudo`， ， `su``runas`
* **破坏性系统操作**：`rm -rf /`、`mkfs`、`dd`、`format`
* **下载和执行模式**：`curl ... | bash`、PowerShell `wget ... | sh``iex (irm ...)`

这些命令在无意中执行时可能会产生不可逆的影响。

### 确定要记录的内容

使用挂钩时，你可捕获 Copilot CLI 在存储库中的使用信息，包括用户提交的提示和 Copilot CLI 尝试运行的工具。

大多数组织至少记录：

* 时间戳和存储库路径
* 提示文本（或经过编辑的形式）
* 工具名称和工具参数
* 任何策略决策（例如，被拒绝的命令及其原因）

请勿在日志中记录机密信息或凭证。 如果提示或命令可能包含敏感数据，请在编写日志之前应用修订。

本教程使用本地 `.github/hooks/logs` 目录作为一个简单的演示示例。 这些日志文件 **不打算提交到存储库** ，通常仅存在于开发人员的计算机上。

在生产环境中，许多组织将事件转发到集中式日志记录或可观测性系统，而不是在本地编写日志。 这样，团队就可以在存储库和用户之间应用一致的编辑、访问控制、保留策略和监控。

### 与利益干系人保持一致

在强制实施策略之前，与以下人员一同审核它们：

* 安全或合规性团队，以确认风险边界
* 平台或基础结构团队，他们可能需要更广泛的权限
* 开发团队，因此他们了解将被阻止的内容以及原因

明确的预期使政策强制实施更容易采用和维护。

## 2.设置存储库挂钩文件

在整个教程中，你将使用存储在仓库中 \*\*\*\* 下的`.github/hooks/`。 每当 Copilot CLI 从该存储库运行时，这些挂钩都会适用。

> \[!NOTE]
> Copilot 代理从 `.github/hooks/*.json` 存储库中的文件加载挂钩配置。 钩子同步运行，可以阻止程序的执行。

### 创建目录结构

在存储库根目录中，为挂钩配置、脚本和日志创建目录：

```bash copy
mkdir -p .github/hooks/scripts
mkdir -p .github/hooks/logs
```

将 `.github/hooks/logs/` 添加到 .gitignore 中，以避免本地审计日志被提交：

```bash copy
echo ".github/hooks/logs/" >> .gitignore
```

本教程使用以下结构：

```text
.github/
└── hooks/
    ├── copilot-cli-policy.json
    ├── logs/
    │   └── audit.jsonl
    └── scripts/
        ├── session-banner.sh
        ├── session-banner.ps1
        ├── log-prompt.sh
        ├── log-prompt.ps1
        ├── pre-tool-policy.sh
        └── pre-tool-policy.ps1
```

> \[!NOTE]
> 本教程旨在创建可在 Windows、Linux 和 macOS 上使用的可移植挂钩配置和脚本。 因此，该 `scripts` 目录将同时包含 Bash 和 PowerShell 脚本，挂钩配置文件将包括 `bash` 和 `powershell` 条目。 CLI 将根据操作系统使用相应的条目。

### 创建挂钩配置文件

在 `.github/hooks/copilot-cli-policy.json` 创建挂钩配置文件。

此文件定义哪些挂钩、何时运行以及执行哪些脚本。

```json copy
{
  "version": 1,
  "hooks": {
    "sessionStart": [
      {
        "type": "command",
        "bash": "./scripts/session-banner.sh",
        "powershell": "./scripts/session-banner.ps1",
        "cwd": ".github/hooks",
        "timeoutSec": 10
      }
    ],
    "userPromptSubmitted": [
      {
        "type": "command",
        "bash": "./scripts/log-prompt.sh",
        "powershell": "./scripts/log-prompt.ps1",
        "cwd": ".github/hooks",
        "timeoutSec": 10
      }
    ],
    "preToolUse": [
      {
        "type": "command",
        "bash": "./scripts/pre-tool-policy.sh",
        "powershell": "./scripts/pre-tool-policy.ps1",
        "cwd": ".github/hooks",
        "timeoutSec": 15
      }
    ]
  }
}
```

### 了解此配置的作用

此配置设置三个挂钩：

* `sessionStart`：显示新代理会话启动或恢复时的信息性消息。
* `userPromptSubmitted`：每当用户提交提示时运行。
* `preToolUse`：在工具执行之前运行，并且可以显式允许或拒绝执行。

### 提交并共享挂钩配置

当您准备好与协作者共享钩子配置时（例如，通过拉取请求或在测试存储库中），请提交钩子配置和脚本。 不要提交任何本地审核日志。

```bash copy
git add .github/hooks/copilot-cli-policy.json .github/hooks/scripts
git commit -m "Add Copilot CLI hook configuration"
git push
```

此时，Copilot CLI 可发现你的挂钩配置，即使你尚未创建挂钩脚本。

## 3. 在会话开始时添加策略横幅

使用 `sessionStart` 挂钩在新 Copilot CLI 会话启动或恢复时显示横幅。 这向开发人员明确表示组织策略处于活动状态。

挂钩函数 `sessionStart` 接收上下文相关信息，例如当前工作目录和初始提示。 此挂钩的任何输出将被 Copilot CLI忽略，这使得它适合信息性消息。

### 创建会话横幅脚本 （Bash）

创建 `.github/hooks/scripts/session-banner.sh`：

```bash copy
#!/bin/bash
set -euo pipefail

cat << 'EOF'
COPILOT CLI POLICY ACTIVE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• Prompts and tool use may be logged for auditing
• High-risk commands may be blocked automatically
• If something is blocked, follow the guidance shown
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EOF
exit 0
```

### 创建会话横幅脚本 （PowerShell）

创建 `.github/hooks/scripts/session-banner.ps1`：

```powershell copy
$ErrorActionPreference = "Stop"

Write-Host @"
COPILOT CLI POLICY ACTIVE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• Prompts and tool use may be logged for auditing
• High-risk commands may be blocked automatically
• If something is blocked, follow the guidance shown
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"@
exit 0
```

### 测试会话横幅

可以直接测试横幅脚本：

```bash
.github/hooks/scripts/session-banner.sh
# or, for PowerShell
.github/hooks/scripts/session-banner.ps1
```

运行任一脚本时，应会看到终端中显示的策略横幅。

## 4.记录提示以用于审计

当用户提交提示到`userPromptSubmitted`时，使用Copilot CLI挂钩进行记录。 每次发送提示时，都会先运行此钩子，然后再调用任何工具。

挂钩接收结构化 JSON 输入，其中包括时间戳、当前工作目录和完整提示文本。 忽略此钩子的输出。

> \[!IMPORTANT]
> 提示可能包含敏感信息。 在记录此数据时应用修订并遵循组织的数据处理和保留策略。

### 创建命令提示符日志记录脚本 （Bash）

创建 `.github/hooks/scripts/log-prompt.sh`：

```bash copy
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"

TIMESTAMP_MS="$(echo "$INPUT" | jq -r '.timestamp // empty')"
CWD="$(echo "$INPUT" | jq -r '.cwd // empty')"

# This example logs only metadata, not the full prompt, to avoid storing
# potentially sensitive data. Adjust to match your organization’s needs.
LOG_DIR=".github/hooks/logs"
mkdir -p "$LOG_DIR"
chmod 700 "$LOG_DIR"

jq -n \
  --arg ts "$TIMESTAMP_MS" \
  --arg cwd "$CWD" \
  '{event:"userPromptSubmitted", timestampMs:$ts, cwd:$cwd}' \
  >> "$LOG_DIR/audit.jsonl"

exit 0
```

### 创建提示日志记录脚本 （PowerShell）

创建 `.github/hooks/scripts/log-prompt.ps1`：

```powershell copy
$ErrorActionPreference = "Stop"

$inputObj = [Console]::In.ReadToEnd() | ConvertFrom-Json

$timestampMs = $inputObj.timestamp
$cwd = $inputObj.cwd
$prompt = $inputObj.prompt

# Optional example redaction. Adjust to match your organization’s needs.
$redactedPrompt = $prompt -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]'

$logDir = ".github/hooks/logs"
if (-not (Test-Path $logDir)) {
  New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}

$logEntry = @{
  event       = "userPromptSubmitted"
  timestampMs = $timestampMs
  cwd         = $cwd
  prompt      = $redactedPrompt
} | ConvertTo-Json -Compress

Add-Content -Path "$logDir/audit.jsonl" -Value $logEntry
exit 0
```

### 测试提示日志记录脚本

你可以通过管道传入示例输入来直接测试脚本。

```bash
echo '{"timestamp":1704614500000,"cwd":"/repo","prompt":"List all branches"}' \
  | .github/hooks/scripts/log-prompt.sh
# or, for PowerShell
echo '{"timestamp":1704614500000,"cwd":"/repo","prompt":"List all branches"}' |
  .github/hooks/scripts/log-prompt.ps1
```

运行脚本后，检查`.github/hooks/logs/audit.jsonl`是否有新的日志条目。

```bash copy
cat .github/hooks/logs/audit.jsonl
```

在此阶段，提交至 Copilot CLI 此存储库的提示会被记录以供审核。

## 5.使用 `preToolUse` 强制执行策略

使用`preToolUse`挂钩来在**工具调用运行之前**进行评估。 此挂钩可以允许执行（不采取任何行动）或拒绝执行（通过返回结构化响应）。

### 了解 `preToolUse` 的输入

`preToolUse` hook 的输入包括：

* `toolName`：即将运行的工具Copilot CLI（例如） `bash`
* `toolArgs`：包含该工具参数的 **JSON 字符串**

因为 `toolArgs` 是 JSON 字符串，所以脚本在读取类似 `command`字段之前必须对其进行分析。

> \[!IMPORTANT]
> 工具参数和命令可能包含敏感信息，例如 API 令牌、密码或其他凭据。 在记录此数据之前应用修订，并遵循组织的安全策略。 请考虑仅记录非敏感元数据（工具名称、时间戳、策略决策），并将审核事件定向到具有适当access控制和保留策略的安全集中日志记录系统。

### 创建策略脚本

接下来，创建策略脚本。 本示例：

* 记录所有尝试的工具使用情况。
* 仅对 bash 命令应用拒绝规则。
* 阻止高风险模式，例如特权升级、破坏性操作和下载并执行命令。

为了安全地验证拒绝流，该脚本还包括一个临时演示规则，用于阻止无害的测试命令。 在确认 hooks 按预期工作后，移除演示规则，并将其替换为符合你组织策略的模式。

#### 示例脚本 （Bash）

创建 `.github/hooks/scripts/pre-tool-policy.sh`：

```bash copy
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"

TOOL_NAME="$(echo "$INPUT" | jq -r '.toolName // empty')"
TOOL_ARGS_RAW="$(echo "$INPUT" | jq -r '.toolArgs // empty')"  # JSON string

LOG_DIR=".github/hooks/logs"
mkdir -p "$LOG_DIR"

# Example redaction logic.
# GitHub does not currently provide built-in secret redaction for hooks.
# This example shows one possible approach; many organizations prefer to
# forward events to a centralized logging system that handles redaction.
# Redact sensitive patterns before logging.
# Adjust these patterns to match your organization's needs.
REDACTED_TOOL_ARGS="$(echo "$TOOL_ARGS_RAW" | \
  sed -E 's/ghp_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \
  sed -E 's/gho_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \
  sed -E 's/ghu_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \
  sed -E 's/ghs_[A-Za-z0-9\._\-]{20,}/[REDACTED_TOKEN]/g' | \
  sed -E 's/Bearer [A-Za-z0-9_\-\.]+/Bearer [REDACTED]/g' | \
  sed -E 's/--password[= ][^ ]+/--password=[REDACTED]/g' | \
  sed -E 's/--token[= ][^ ]+/--token=[REDACTED]/g')"

# Log attempted tool use with redacted toolArgs.
jq -n \
  --arg tool "$TOOL_NAME" \
  --arg toolArgs "$REDACTED_TOOL_ARGS" \
  '{event:"preToolUse", toolName:$tool, toolArgs:$toolArgs}' \
  >> "$LOG_DIR/audit.jsonl"

# Only enforce command rules for bash.
if [ "$TOOL_NAME" != "bash" ]; then
  exit 0
fi

# Parse toolArgs JSON string.
# If toolArgs isn't valid JSON for some reason, allow (and rely on logs).
if ! echo "$TOOL_ARGS_RAW" | jq -e . >/dev/null 2>&1; then
  exit 0
fi

COMMAND="$(echo "$TOOL_ARGS_RAW" | jq -r '.command // empty')"

# ---------------------------------------------------------------------------
# Demo-only deny rule for safe testing.
# This blocks a harmless test command so you can validate the deny flow.
# Remove this rule after confirming your hooks work as expected.
# ---------------------------------------------------------------------------
if echo "$COMMAND" | grep -q "COPILOT_HOOKS_DENY_DEMO"; then
  deny "Blocked demo command (test rule). Remove this rule after validating hooks."
fi

deny() {
  local reason="$1"

  # Redact sensitive patterns from command before logging.
  local redacted_cmd="$(echo "$COMMAND" | \
    sed -E 's/ghp_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \
    sed -E 's/gho_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \
    sed -E 's/ghu_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \
    sed -E 's/ghs_[A-Za-z0-9\.\-_]{20,}/[REDACTED_TOKEN]/g' | \
    sed -E 's/Bearer [A-Za-z0-9_\-\.]+/Bearer [REDACTED]/g' | \
    sed -E 's/--password[= ][^ ]+/--password=[REDACTED]/g' | \
    sed -E 's/--token[= ][^ ]+/--token=[REDACTED]/g')"

  # Log the denial decision with redacted command.
  jq -n \
    --arg cmd "$redacted_cmd" \
    --arg r "$reason" \
    '{event:"policyDeny", toolName:"bash", command:$cmd, reason:$r}' \
    >> "$LOG_DIR/audit.jsonl"

  # Return a denial response.
  jq -n \
    --arg r "$reason" \
    '{permissionDecision:"deny", permissionDecisionReason:$r}'

  exit 0
}

# Privilege escalation
if echo "$COMMAND" | grep -qE '\b(sudo|su|runas)\b'; then
  deny "Privilege escalation requires manual approval."
fi

# Destructive filesystem operations targeting root
if echo "$COMMAND" | grep -qE 'rm\s+-rf\s*/($|\s)|rm\s+.*-rf\s*/($|\s)'; then
  deny "Destructive operations targeting the filesystem root require manual approval."
fi

# System-level destructive operations
if echo "$COMMAND" | grep -qE '\b(mkfs|dd|format)\b'; then
  deny "System-level destructive operations are not allowed via automated execution."
fi

# Download-and-execute patterns
if echo "$COMMAND" | grep -qE 'curl.*\|\s*(bash|sh)|wget.*\|\s*(bash|sh)'; then
  deny "Download-and-execute patterns require manual approval."
fi

# Allow by default
exit 0
```

#### 创建策略脚本 （PowerShell）

创建 `.github/hooks/scripts/pre-tool-policy.ps1`：

```powershell copy
$ErrorActionPreference = "Stop"

$inputObj = [Console]::In.ReadToEnd() | ConvertFrom-Json
$toolName = $inputObj.toolName
$toolArgsRaw = $inputObj.toolArgs  # JSON string

$logDir = ".github/hooks/logs"
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }

# Example redaction logic.
# GitHub does not currently provide built-in secret redaction for hooks.
# This example shows one possible approach; many organizations prefer to
# forward events to a centralized logging system that handles redaction.
# Redact sensitive patterns before logging.
# Adjust these patterns to match your organization's needs.
$redactedToolArgs = $toolArgsRaw `
  -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
  -replace 'gho_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
  -replace 'ghu_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
  -replace 'ghs_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
  -replace 'Bearer [A-Za-z0-9_\-\.]+', 'Bearer [REDACTED]' `
  -replace '--password[= ][^ ]+', '--password=[REDACTED]' `
  -replace '--token[= ][^ ]+', '--token=[REDACTED]'

# Log attempted tool use with redacted toolArgs.
(@{
  event    = "preToolUse"
  toolName = $toolName
  toolArgs = $redactedToolArgs
} | ConvertTo-Json -Compress) | Add-Content -Path "$logDir/audit.jsonl"

if ($toolName -ne "bash") { exit 0 }

# Parse toolArgs JSON string.
$toolArgs = $null
try { $toolArgs = $toolArgsRaw | ConvertFrom-Json } catch { exit 0 }

$command = $toolArgs.command

# ---------------------------------------------------------------------------
# Demo-only deny rule for safe testing.
# This blocks a harmless test command so you can validate the deny flow.
# Remove this rule after confirming your hooks work as expected.
# ---------------------------------------------------------------------------
if ($command -match 'COPILOT_HOOKS_DENY_DEMO') {
  Deny "Blocked demo command (test rule). Remove this rule after validating hooks."
}

function Deny([string]$reason) {
  # Redact sensitive patterns from command before logging.
  $redactedCommand = $command `
    -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
    -replace 'gho_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
    -replace 'ghu_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
    -replace 'ghs_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `
    -replace 'Bearer [A-Za-z0-9_\-\.]+', 'Bearer [REDACTED]' `
    -replace '--password[= ][^ ]+', '--password=[REDACTED]' `
    -replace '--token[= ][^ ]+', '--token=[REDACTED]'

  # Log the denial decision with redacted command.
  (@{
    event    = "policyDeny"
    toolName = "bash"
    command  = $redactedCommand
    reason   = $reason
  } | ConvertTo-Json -Compress) | Add-Content -Path "$logDir/audit.jsonl"

  (@{
    permissionDecision = "deny"
    permissionDecisionReason = $reason
  } | ConvertTo-Json -Compress)

  exit 0
}

if ($command -match '\b(sudo|su|runas)\b') { Deny "Privilege escalation requires manual approval." }
if ($command -match 'rm\s+-rf\s*/(\s|$)|rm\s+.*-rf\s*/(\s|$)') { Deny "Destructive operations targeting the filesystem root require manual approval." }
if ($command -match '\b(mkfs|dd|format)\b') { Deny "System-level destructive operations are not allowed via automated execution." }
if ($command -match 'curl.*\|\s*(bash|sh)|wget.*\|\s*(bash|sh)') { Deny "Download-and-execute patterns require manual approval." }

exit 0
```

### 测试策略脚本

你可以通过管道传入示例 `preToolUse` 输入来测试脚本。

允许示例：

```bash
echo '{"toolName":"bash","toolArgs":"{\"command\":\"git status\"}"}' \
  | .github/hooks/scripts/pre-tool-policy.sh
# or, for PowerShell
echo '{"toolName":"bash","toolArgs":"{\"command\":\"git status\"}"}' |
  .github/hooks/scripts/pre-tool-policy.ps1
```

拒绝示例：

```bash
echo '{"toolName":"bash","toolArgs":"{\"command\":\"sudo rm -rf /\"}"}' \
  | .github/hooks/scripts/pre-tool-policy.sh
# or, for PowerShell
echo '{"toolName":"bash","toolArgs":"{\"command\":\"sudo rm -rf /\"}"}' |
  .github/hooks/scripts/pre-tool-policy.ps1
```

运行拒绝示例后，检查`.github/hooks/logs/audit.jsonl`是否有新的拒绝日志条目。

```json
{"permissionDecision":"deny","permissionDecisionReason":"Privilege escalation requires manual approval."}
```

此时，会阻止高风险 `bash` 命令在此存储库中自动执行。

## 6. 在存储库中端到端测试

创建配置文件和脚本后，验证在此存储库中使用 Copilot CLI 时挂钩按预期运行。

### 验证挂钩配置文件

检查挂钩配置文件是否为有效的 JSON：

```bash copy
jq '.' < .github/hooks/copilot-cli-policy.json
```

### 验证脚本权限（基于 Unix 的系统）

在 macOS 和 Linux 上，确认 Bash 脚本是可执行的：

```bash copy
chmod +x .github/hooks/scripts/*.sh
```

### 运行基本会话

在存储库中启动新 Copilot CLI 会话：

```bash copy
copilot -p "Show me the status of this repository"
```

预期结果：

* 你将看到策略横幅（来自 `sessionStart`）。
* 将新条目添加到 `.github/hooks/logs/audit.jsonl`（从 `userPromptSubmitted`）。

### 触发工具的使用与日志记录验证

运行一个提示，以使 Copilot CLI 使用工具（例如 bash）：

```bash copy
copilot -p "Show me the last 5 git commits"
```

预期结果：

* `preToolUse` 条目将添加到 `.github/hooks/logs/audit.jsonl`。
* 如果允许工具调用，则执行将正常进行。

### 测试被拒绝的命令

示例策略脚本包括一个临时演示规则，用于阻止包含字符串 `COPILOT_HOOKS_DENY_DEMO`的命令。 这样就可以安全地验证拒绝流，而无需运行破坏性命令。

运行一个会导致命令被拒绝的提示：

```bash copy
copilot -p "Run a test command: echo COPILOT_HOOKS_DENY_DEMO"
```

预期结果：

* Copilot CLI 不执行命令。
* 钩子函数返回一个拒绝响应，并附有明确的原因。
* `policyDeny` 条目写入 `.github/hooks/logs/audit.jsonl`。

确认拒绝流正常工作后，请从脚本中删除演示规则，并将其替换为反映组织策略的拒绝模式。

### 检查审核日志

查看最近的条目：

```bash copy
tail -n 50 .github/hooks/logs/audit.jsonl
```

仅筛选被拒绝的决定：

```bash copy
jq 'select(.event=="policyDeny")' .github/hooks/logs/audit.jsonl
```

## 7. 跨团队安全部署

在单个存储库中验证钩子后，逐步推出以避免中断开发工作流。

### 选择推出策略

常见的推出方法包括：

* **日志记录优先推出（建议）**：首先记录提示和工具使用情况，而无需拒绝执行。 查看一段时间的日志，然后在了解常见使用模式后引入拒绝规则。
* **团队部署**：一次将挂钩部署到一个团队或存储库，收集反馈，然后扩展到其他团队。
* **基于风险的推出**：从处理敏感系统或生产基础结构的存储库开始，然后扩展到风险较低的存储库。

### 传达期望

在强制实施拒绝规则之前，请确保开发人员了解：

* 这表明 hooks 已在仓库中处于活动状态
* 可以阻止哪些类型的命令
* 如果执行命令被拒绝，应如何继续操作

清晰的沟通从而减少混淆和支持请求。

### 使策略保持可维护

随着使用情况的发展：

* 将挂钩配置和脚本存储在版本控制中。
* 定期查看审核日志，以检测新的风险模式。
* 请逐步更新拒绝规则，而不要一次性添加范围过大的匹配规则。
* 记录每个拒绝规则存在的原因，尤其是对于高影响限制。

### 小心处理异常

某些团队（例如基础结构或平台团队）可能需要更广泛的权限。 若要安全地处理此问题，

* 维护不同存储库的单独挂钩配置。
* 确保例外范围较窄且记录完善。
* 避免临时性的本地绕过，这会削弱审计透明度。

## 延伸阅读

有关 hooks 的故障排查，请参阅 [使用挂钩自定义代理工作流](/zh/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks#troubleshooting)。