Este tutorial é para especialistas em DevOps, equipes de plataforma e líderes de engenharia dando suporte a desenvolvedores que usam CLI do Copilot.
Hooks são scripts personalizados que são executados em pontos específicos durante uma sessão CLI do Copilot. Eles podem inspecionar prompts e chamadas de ferramenta, registrar informações para auditoria e até bloquear a execução de determinados comandos.
Você configurará ganchos com escopo de repositório que:
- Forneça visibilidade sobre prompts e uso de ferramentas.
- Bloqueie padrões de comando de alto risco antes da execução.
- Ajude os desenvolvedores a entender as políticas organizacionais com mensagens claras.
Pré-requisitos
- Familiaridade com scripts de shell (Bash ou PowerShell)
- Compreensão básica dos arquivos de configuração JSON
- Acesso a um repositório onde CLI do Copilot é usado
-
`jq` instalado (para os exemplos do Bash)
1. Definir uma política organizacional
Antes de escrever quaisquer scripts de hook, decida quais ações devem ser permitidas automaticamente e quais devem exigir revisão humana.
Uma política clara ajuda você a evitar o bloqueio excessivo enquanto ainda reduz o risco.
Identificar comandos que sempre exigem revisão
Comece identificando padrões que nunca devem ser executados automaticamente por CLI do Copilot. Exemplos comuns incluem:
-
**Escalonamento de privilégios**: `sudo`, , `su``runas` -
**Operações destrutivas do sistema**: `rm -rf /`, , `mkfs`, `dd``format` -
**Padrões de download e execução**: `curl ... | bash`, , `wget ... | sh`PowerShell `iex (irm ...)`
Esses comandos podem ter efeitos irreversíveis se executados sem querer.
Decidir o que registrar em log
Ao usar ganchos, você pode capturar informações sobre como CLI do Copilot é utilizado em um repositório, incluindo prompts enviados por usuários e ferramentas que CLI do Copilot tenta executar.
No mínimo, a maioria das organizações registra:
- O timestamp e o caminho do repositório
- O texto do prompt (ou um formulário redigido)
- O nome da ferramenta e os argumentos da ferramenta
- Qualquer decisão de política (por exemplo, um comando negado e seu motivo)
Evite logar segredos ou credenciais. Se os prompts ou comandos contiverem dados confidenciais, aplique a redação antes de gravar logs.
Este tutorial usa um diretório local .github/hooks/logs como um exemplo simples e ilustrativo. Esses arquivos de log não são destinados à confirmação no repositório e normalmente existem apenas no computador de um desenvolvedor.
Em ambientes de produção, muitas organizações encaminham eventos de gancho para um sistema centralizado de registro em log ou observabilidade em vez de gravar logs localmente. Isso permite que as equipes apliquem redação consistente, controles de acesso, políticas de retenção e monitoramento entre repositórios e usuários.
Alinhar com as partes interessadas
Antes de impor políticas, revise-as com:
- Equipes de segurança ou conformidade, para confirmar os limites de risco
- Equipes de plataforma ou infraestrutura, que podem precisar de permissões mais amplas
- Equipes de desenvolvimento, para que entendam o que será bloqueado e por que
Expectativas claras facilitam a aplicação e manutenção da política.
2. Configurar arquivos de gancho de repositório
Ao longo deste tutorial, você usará ganchos com escopo de repositório armazenados no repositório sob .github/hooks/. Esses ganchos se aplicam sempre que CLI do Copilot é executado dentro desse repositório.
Observação
Copilot agentes carregam arquivos de configuração de ganchos do repositório .github/hooks/*.json. Os ganchos são executados de forma síncrona e podem bloquear a execução.
Criar a estrutura do diretório
Na raiz do repositório, crie diretórios para sua configuração de gancho, scripts e logs:
mkdir -p .github/hooks/scripts mkdir -p .github/hooks/logs
mkdir -p .github/hooks/scripts
mkdir -p .github/hooks/logs
Adicione .github/hooks/logs/ ao .gitignore para que os logs de auditoria locais não sejam confirmados:
echo ".github/hooks/logs/" >> .gitignore
echo ".github/hooks/logs/" >> .gitignore
Este tutorial usa a seguinte estrutura:
.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
Criar um arquivo de configuração de gancho
Crie um arquivo de configuração de gancho em .github/hooks/copilot-cli-policy.json.
Esse arquivo define quais ganchos são executados, quando são executados e quais scripts eles executam.
{
"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
}
]
}
}
{
"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
}
]
}
}
Entender o que essa configuração faz
Essa configuração configura três ganchos:
-
`sessionStart`: mostra uma mensagem informativa quando uma nova sessão do agente é iniciada ou retomada. -
`userPromptSubmitted`: é executado sempre que um usuário envia um prompt. -
`preToolUse`: é executado antes que uma ferramenta seja executada e possa permitir ou negar explicitamente a execução.
Confirmar e compartilhar a configuração do gancho
Quando estiver pronto para compartilhar a configuração de gancho com colaboradores (por exemplo, por meio de uma solicitação de pull ou em um repositório de teste), confirme a configuração do gancho e os scripts. Não confirme nenhum log de auditoria local.
git add .github/hooks/copilot-cli-policy.json .github/hooks/scripts git commit -m "Add Copilot CLI hook configuration" git push
git add .github/hooks/copilot-cli-policy.json .github/hooks/scripts
git commit -m "Add Copilot CLI hook configuration"
git push
Neste ponto, CLI do Copilot pode descobrir sua configuração de hooks, mesmo que você ainda não tenha criado os scripts de hooks.
3. Adicionar um banner de política no início da sessão
Use um sessionStart gancho para exibir um banner sempre que uma nova sessão CLI do Copilot for iniciada ou retomada. Isso deixa claro para os desenvolvedores que as políticas organizacionais estão ativas.
O sessionStart gancho recebe informações contextuais, como o diretório de trabalho atual e o prompt inicial. Qualquer saída desse hook é ignorada por CLI do Copilot, o que o torna adequado para mensagens informativas.
Criar o script do banner de sessão (Bash)
Criar .github/hooks/scripts/session-banner.sh:
#!/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
#!/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
Criar o script de banner de sessão (PowerShell)
Criar .github/hooks/scripts/session-banner.ps1:
$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
$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
Testar o banner de sessão
Você pode testar os scripts de banner diretamente:
.github/hooks/scripts/session-banner.sh
# or, for PowerShell
.github/hooks/scripts/session-banner.ps1
Ao executar qualquer um dos scripts, você deverá ver o banner de política exibido no terminal.
4. Solicitações de log para auditoria
Use o userPromptSubmitted hook para registrar quando os usuários enviam prompts para CLI do Copilot. Esse gancho é executado sempre que um prompt é enviado, antes que qualquer ferramenta seja invocada.
O gancho recebe entrada JSON estruturada que inclui o carimbo de data/hora, o diretório de trabalho atual e o texto completo do prompt. A saída desse gancho é ignorada.
Importante
Os prompts podem conter informações confidenciais. Aplique a redação e siga as políticas de tratamento e retenção de dados da sua organização ao registrar esses dados em log.
Criar o script de registro em log do prompt (Bash)
Criar .github/hooks/scripts/log-prompt.sh:
#!/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
#!/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
Criar o script de registro em log do prompt (PowerShell)
Criar .github/hooks/scripts/log-prompt.ps1:
$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
$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
Testar o script de registro em log do prompt
Você pode testar os scripts diretamente por meio da entrada de exemplo de tubulação.
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
Depois de executar o script, verifique .github/hooks/logs/audit.jsonl se há uma nova entrada de log.
cat .github/hooks/logs/audit.jsonl
cat .github/hooks/logs/audit.jsonl
Neste momento, os prompts enviados para CLI do Copilot neste repositório são registrados para auditoria.
5. Aplicar políticas com preToolUse
Use o preToolUse gancho para avaliar uma chamada de ferramenta antes de ser executada. Esse gancho pode permitir a execução (sem fazer nada) ou negar a execução (retornando uma resposta estruturada).
Entender a entrada preToolUse
A preToolUse entrada do gancho inclui:
-
`toolName`: A ferramenta que CLI do Copilot está prestes a executar (por exemplo, `bash`) -
`toolArgs`: uma cadeia de caracteres **JSON** que contém os argumentos dessa ferramenta
Como toolArgs é uma cadeia de caracteres JSON, seu script deve analisá-lo antes de ler campos como command.
Importante
Os argumentos e comandos da ferramenta podem conter informações confidenciais, como tokens de API, senhas ou outras credenciais. Aplique a redação antes de registrar esses dados em log e siga as políticas de segurança da sua organização. Considere registrar apenas metadados não confidenciais (nome da ferramenta, carimbo de data/hora, decisão de política) e direcionar eventos de auditoria para um sistema de registro em log protegido e centralizado com controles de acesso e políticas de retenção apropriados.
Criar o script de política
Em seguida, crie um script de política. Neste exemplo:
- Registra todas as tentativas de uso da ferramenta.
- Aplica regras de negação somente a comandos bash.
- Bloqueia padrões de alto risco, como escalonamento de privilégios, operações destrutivas e comandos de download e execução.
Para permitir que você valide o fluxo de negação com segurança, o script também inclui uma regra de demonstração temporária que bloqueia um comando de teste inofensivo. Depois de confirmar que os ganchos funcionam conforme o esperado, remova a regra de demonstração e substitua-a por padrões que refletem as políticas da sua organização.
Script de exemplo (Bash)
Criar .github/hooks/scripts/pre-tool-policy.sh:
#!/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
#!/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
Criar o script de política (PowerShell)
Criar .github/hooks/scripts/pre-tool-policy.ps1:
$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
$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
Testar o script de política
Você pode testar os scripts encaminhando a entrada de exemplo preToolUse.
Exemplo de permissão:
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
Exemplo de negação:
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
Depois de executar o exemplo de negação, verifique .github/hooks/logs/audit.jsonl se há uma nova entrada de log de negação.
{"permissionDecision":"deny","permissionDecisionReason":"Privilege escalation requires manual approval."}
Neste ponto, os comandos de alto risco bash são bloqueados da execução automática neste repositório.
6. Testar de ponta a ponta no repositório
Depois de criar o arquivo de configuração e os scripts, verifique se os hooks são executados conforme o esperado quando você usa CLI do Copilot neste repositório.
Validar o arquivo de configuração do hook
Verifique se o arquivo de configuração do gancho é JSON válido:
jq '.' < .github/hooks/copilot-cli-policy.json
jq '.' < .github/hooks/copilot-cli-policy.json
Verificar permissões de script (sistemas baseados em Unix)
No macOS e no Linux, confirme se os scripts bash são executáveis:
chmod +x .github/hooks/scripts/*.sh
chmod +x .github/hooks/scripts/*.sh
Executar uma sessão básica
Inicie uma nova sessão CLI do Copilot no repositório:
copilot -p "Show me the status of this repository"
copilot -p "Show me the status of this repository"
Resultados esperados:
- Você vê o banner de política (de
sessionStart). - Uma nova entrada é adicionada a
.github/hooks/logs/audit.jsonl(deuserPromptSubmitted).
Acionar a ferramenta e verificar o log
Execute um comando para que CLI do Copilot use uma ferramenta (por exemplo, bash):
copilot -p "Show me the last 5 git commits"
copilot -p "Show me the last 5 git commits"
Resultados esperados:
- Uma
preToolUseentrada é adicionada a.github/hooks/logs/audit.jsonl. - Se a chamada de ferramenta for permitida, a execução continuará normalmente.
Testar um comando negado
O script de política de exemplo inclui uma regra de demonstração temporária que bloqueia os comandos que contêm a cadeia de caracteres COPILOT_HOOKS_DENY_DEMO. Isso permite que você valide o fluxo de negação com segurança sem executar comandos destrutivos.
Execute um prompt que dispararia um comando negado:
copilot -p "Run a test command: echo COPILOT_HOOKS_DENY_DEMO"
copilot -p "Run a test command: echo COPILOT_HOOKS_DENY_DEMO"
Resultados esperados:
- CLI do Copilot não executa o comando.
- Seu gancho retorna uma resposta de negação com um motivo claro.
- Uma
policyDenyentrada é gravada em.github/hooks/logs/audit.jsonl.
Depois de confirmar que o fluxo de negação funciona corretamente, remova a regra de demonstração do script e substitua-a por padrões de negação que reflitam as políticas da sua organização.
Inspecionar seus logs de auditoria
Para exibir entradas recentes:
tail -n 50 .github/hooks/logs/audit.jsonl
tail -n 50 .github/hooks/logs/audit.jsonl
Para filtrar somente as decisões negadas:
jq 'select(.event=="policyDeny")' .github/hooks/logs/audit.jsonl
jq 'select(.event=="policyDeny")' .github/hooks/logs/audit.jsonl
7. Distribuir com segurança entre as equipes
Depois de validar seus ganchos em um único repositório, implemente-os gradualmente para evitar interromper os fluxos de trabalho de desenvolvimento.
Escolher uma estratégia de distribuição
As abordagens comuns de distribuição incluem:
-
**Início baseado em registros de log (recomendado)**: Inicie registrando prompts e o uso da ferramenta sem impedir a execução. Examine os logs por um período de tempo e, em seguida, introduza regras de negação depois de entender os padrões de uso comuns. -
**Implantação por equipe**: Desenvolver ganchos em uma equipe ou repositório de cada vez, coletar feedback e expandir para equipes adicionais. -
**Distribuição baseada em risco**: comece com repositórios que lidam com sistemas confidenciais ou infraestrutura de produção e expandam para repositórios de menor risco.
Comunicar expectativas
Antes de impor regras de negação, certifique-se de que os desenvolvedores entendam:
- Esses ganchos estão ativos no repositório
- Quais tipos de comandos podem ser bloqueados
- Como proceder se um comando for negado
A comunicação clara reduz a confusão e as solicitações de suporte.
Manter as políticas mantenedíveis
Conforme o uso evolui:
- Armazenar a configuração do gancho e scripts no controle de versão.
- Examine os logs de auditoria periodicamente para detectar novos padrões de risco.
- Atualize as regras de negação incrementalmente em vez de adicionar correspondências amplas.
- Documente por que cada regra de negação existe, especialmente para restrições de alto impacto.
Lidar com exceções com cuidado
Algumas equipes (por exemplo, equipes de infraestrutura ou plataforma) podem exigir permissões mais amplas. Para lidar com isso com segurança:
- Mantenha configurações de gancho separadas para repositórios diferentes.
- Mantenha as exceções estreitas e bem documentadas.
- Evite bypasses locais ad hoc que comprometam a auditabilidade.
Leitura adicional
Para solucionar problemas de ganchos, consulte Uso de ganchos com agentes do GitHub Copilot.