Este tutorial es para ingenieros de DevOps, equipos de plataforma y líderes de ingeniería que apoyan a los desarrolladores que usan CLI de Copilot.
Los enlaces son scripts personalizados que se ejecutan en puntos específicos durante una sesión de CLI de Copilot. Pueden inspeccionar las indicaciones y las llamadas a herramientas, los registros de auditoría e incluso bloquear la ejecución de ciertos comandos.
Configurará enlaces con ámbito de repositorio que:
- Proporcionar visibilidad de las indicaciones y el uso de herramientas.
- Bloquear patrones de comandos de alto riesgo antes de la ejecución.
- Ayude a los desarrolladores a comprender las directivas de la organización con mensajería clara.
Prerrequisitos
- Familiaridad con el scripting de shell (Bash o PowerShell)
- Conocimientos básicos de los archivos de configuración JSON
- Acceso a un repositorio donde se usa CLI de Copilot
-
`jq` instalado (para los ejemplos de Bash)
1. Definir una directiva organizativa
Antes de escribir scripts de enlace, decida qué acciones se deben permitir automáticamente y cuáles deben requerir revisión humana.
Una directiva clara le ayuda a evitar el exceso de bloqueo mientras sigue reduciendo el riesgo.
Identificación de comandos que siempre requieren revisión
Empiece por identificar patrones que nunca se deben ejecutar automáticamente mediante CLI de Copilot. Algunos ejemplos habituales:
-
**Escalación de privilegios**: `sudo`, `su`, `runas` -
**Operaciones destructivas del sistema**: `rm -rf /`, `mkfs`, `dd`, `format` -
**Patrones de descarga y ejecución**: `curl ... | bash`, `wget ... | sh`, PowerShell `iex (irm ...)`
Estos comandos pueden tener efectos irreversibles si se ejecutan involuntariamente.
Decidir qué registrar
Al usar enlaces, puede capturar información sobre cómo CLI de Copilot se usa en un repositorio, incluidas las solicitudes enviadas por los usuarios y las herramientas que CLI de Copilot intenta ejecutar.
Como mínimo, la mayoría de las organizaciones registran:
- La marca de tiempo y la ruta de acceso del repositorio
- Texto del mensaje (o un formulario redactado)
- El nombre de la herramienta y los argumentos de la herramienta
- Cualquier decisión de directiva (por ejemplo, un comando denegado y su motivo)
Evite registrar secretos o credenciales. Si los mensajes o comandos pueden contener datos confidenciales, aplique la redacción antes de escribir registros.
En este tutorial se usa un directorio local .github/hooks/logs como ejemplo ilustrativo sencillo. Estos archivos de registro no están diseñados para subirse al repositorio y normalmente viven solo en la máquina de un desarrollador.
En entornos de producción, muchas organizaciones reenvía eventos de enlace a un sistema centralizado de registro o observabilidad en lugar de escribir registros localmente. Esto permite a los equipos aplicar revisiones coherentes, controles de acceso, directivas de retención y supervisión en repositorios y usuarios.
Alineación con las partes interesadas
Antes de aplicar políticas, repáselas con:
- Equipos de seguridad o cumplimiento para confirmar límites de riesgo
- Equipos de plataforma o infraestructura, que pueden necesitar permisos más amplios
- Equipos de desarrollo, para que comprendan qué se bloqueará y por qué
Las expectativas claras facilitan la adopción y el mantenimiento de las directivas.
2. Configurar archivos de enlace de repositorio
En este tutorial, utilizará enlaces con ámbito de repositorio almacenados en el repositorio, en .github/hooks/. Estos enlaces se aplican cada vez que CLI de Copilot se ejecuta desde este repositorio.
Nota:
Los agentes de Copilot cargan los archivos de configuración de enlaces desde .github/hooks/*.json en el repositorio. Los hooks se ejecutan sincrónicamente y pueden bloquear la ejecución.
Creación de la estructura de directorios
En la raíz del repositorio, cree directorios para la configuración del hook, los scripts y los registros.
mkdir -p .github/hooks/scripts mkdir -p .github/hooks/logs
mkdir -p .github/hooks/scripts
mkdir -p .github/hooks/logs
Agregue .github/hooks/logs/ en .gitignore para que no se comprometan los registros de auditoría locales.
echo ".github/hooks/logs/" >> .gitignore
echo ".github/hooks/logs/" >> .gitignore
En este tutorial se usa la siguiente estructura:
.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
Creación de un archivo de configuración de enlace
Cree un archivo de configuración de hook en .github/hooks/copilot-cli-policy.json.
Este archivo define cuáles ganchos se ejecutan, cuándo se activan y qué scripts ejecutan.
{
"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
}
]
}
}
Comprender lo que hace esta configuración
Esta configuración configura tres ganchos:
-
`sessionStart`: muestra un mensaje informativo cuando se inicia o reanuda una nueva sesión del agente. -
`userPromptSubmitted`: se ejecuta cada vez que un usuario envía un mensaje. -
`preToolUse`: se ejecuta antes de que una herramienta se ejecute y pueda permitir o denegar explícitamente la ejecución.
Confirmar y compartir la configuración del enlace
Cuando esté listo para compartir la configuración de enlace con colaboradores (por ejemplo, a través de una solicitud de incorporación de cambios o en un repositorio de pruebas), confirme la configuración y los scripts del enlace. No guarde ningún registro de auditoría 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
En este momento, CLI de Copilot puede detectar la configuración del enlace, aunque aún no haya creado los scripts de enlace.
3. Agregar un banner de directiva al inicio de la sesión
Use un enlace sessionStart para mostrar un banner cada vez que se inicie o reanude una nueva sesión CLI de Copilot. Esto deja claro a los desarrolladores que las directivas organizativas están activas.
El enlace sessionStart recibe información contextual, como el directorio de trabajo actual y la solicitud inicial. CLI de Copilot ignora cualquier salida de este enlace, lo que lo hace adecuado para mensajes informativos.
Creación del script de banner de sesión (Bash)
Crear .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
Creación del script de banner de sesión (PowerShell)
Crear .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
Probar el banner de sesión
Puede probar los scripts de banner directamente:
.github/hooks/scripts/session-banner.sh
# or, for PowerShell
.github/hooks/scripts/session-banner.ps1
Al ejecutar cualquiera de los scripts, debería ver el banner de directiva que se muestra en su terminal.
4. Registrar las solicitudes para auditoría
Use el enlace userPromptSubmitted para registrar el momento en que los usuarios envían solicitudes a CLI de Copilot. Este gancho se ejecuta cada vez que se envía un indicador, antes de invocar cualquier herramienta.
El enlace recibe una entrada JSON estructurada que incluye la marca de tiempo, el directorio de trabajo actual y el texto completo de la solicitud. La salida de este enlace se ignora.
Importante
Las indicaciones pueden contener información confidencial. Aplique la redacción y siga las directivas de retención y manejo de datos de su organización al registrar estos datos.
Creación del script de registro de solicitudes (Bash)
Crear .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
Crear el script de registro de comandos (PowerShell)
Crear .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
Prueba del script de registro de solicitudes
Puede probar los scripts directamente canalizando la entrada de ejemplo.
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
Después de ejecutar el script, compruebe .github/hooks/logs/audit.jsonl si hay una nueva entrada de registro.
cat .github/hooks/logs/audit.jsonl
cat .github/hooks/logs/audit.jsonl
En este momento, las solicitudes enviadas a CLI de Copilot en este repositorio se registran para procesos de auditoría.
5. Aplicar directivas con preToolUse
Use el preToolUse gancho para evaluar una llamada de herramienta antes de que se ejecute. Este enlace puede permitir la ejecución (sin hacer nada) o denegar la ejecución (devolviendo una respuesta estructurada).
Comprenda la preToolUse entrada
La entrada del enlace preToolUse incluye:
-
`toolName`: la herramienta que CLI de Copilot está a punto de ejecutar (por ejemplo, `bash`) -
`toolArgs`: **una cadena JSON** que contiene los argumentos de esa herramienta.
Dado que toolArgs es una cadena JSON, el script debe analizarlo antes de leer campos como command.
Importante
Los argumentos y comandos de la herramienta pueden contener información confidencial, como tokens de API, contraseñas u otras credenciales. Aplique la redacción antes de registrar estos datos y siga las políticas de seguridad de su organización. Considere la posibilidad de registrar solo metadatos no confidenciales (nombre de herramienta, marca de tiempo, decisión de directiva) y dirigir eventos de auditoría a un sistema de registro seguro y centralizado con los controles de acceso y las directivas de retención adecuados.
Creación del script de directiva
A continuación, cree un script de directiva. En este ejemplo:
- Registra todos los intentos de uso de herramientas.
- Aplica reglas de denegación solo a los comandos de Bash.
- Bloquea patrones de alto riesgo, como la elevación de privilegios, las operaciones destructivas y los comandos de descarga y ejecución.
Para permitir validar el flujo de denegación de forma segura, el script también incluye una regla de demostración temporal que bloquea un comando de prueba inofensivo. Después de confirmar que los enlaces funcionan según lo previsto, quite la regla de demostración y reemplácela por patrones que reflejen las directivas de la organización.
Script de ejemplo (Bash)
Crear .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
Creación del script de directiva (PowerShell)
Crear .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
Prueba del script de directiva
Puede probar los scripts canalizando la entrada de ejemplo preToolUse.
Permitir ejemplo:
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
Ejemplo de denegación:
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
Después de ejecutar el ejemplo de denegación, compruebe .github/hooks/logs/audit.jsonl si hay una nueva entrada de registro de denegación.
{"permissionDecision":"deny","permissionDecisionReason":"Privilege escalation requires manual approval."}
En este momento, los comandos de alto riesgo bash se bloquean para la ejecución automática en este repositorio.
6. Prueba de un extremo a otro en el repositorio
Una vez que haya creado el archivo de configuración y los scripts, compruebe que los enlaces se ejecutan según lo previsto al usar CLI de Copilot en este repositorio.
Valida tu archivo de configuración de hook
Compruebe que el archivo de configuración de enlace sea json válido:
jq '.' < .github/hooks/copilot-cli-policy.json
jq '.' < .github/hooks/copilot-cli-policy.json
Comprobación de los permisos de script (sistemas basados en Unix)
En macOS y Linux, confirme que los scripts de Bash son ejecutables:
chmod +x .github/hooks/scripts/*.sh
chmod +x .github/hooks/scripts/*.sh
Ejecución de una sesión básica
Inicie una nueva sesión de CLI de Copilot en el repositorio:
copilot -p "Show me the status of this repository"
copilot -p "Show me the status of this repository"
Resultados esperados:
- Ves el banner de política (de
sessionStart). - Se agrega una nueva entrada a
.github/hooks/logs/audit.jsonl(deuserPromptSubmitted).
Uso de la herramienta de desencadenamiento y verificación del registro
Ejecute una solicitud que haga que CLI de Copilot use una herramienta (por ejemplo, Bash):
copilot -p "Show me the last 5 git commits"
copilot -p "Show me the last 5 git commits"
Resultados esperados:
- Se agrega una
preToolUseentrada a.github/hooks/logs/audit.jsonl. - Si se permite la llamada a la herramienta, la ejecución continúa normalmente.
Prueba de un comando denegado
El script de directiva de ejemplo incluye una regla de demostración temporal que bloquea los comandos que contienen la cadena COPILOT_HOOKS_DENY_DEMO. Esto le permite validar el flujo de denegación de forma segura sin ejecutar comandos destructivos.
Ejecute un mensaje que provocaría un comando denegado:
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 de Copilot no ejecuta el comando.
- El enlace devuelve una respuesta de denegación con un motivo claro.
- Una
policyDenyentrada se escribe en.github/hooks/logs/audit.jsonl.
Después de confirmar que el flujo de denegación funciona correctamente, quite la regla de demostración del script y reemplácela por patrones de denegación que reflejen las directivas de la organización.
Inspección de los registros de auditoría
Para ver las entradas recientes:
tail -n 50 .github/hooks/logs/audit.jsonl
tail -n 50 .github/hooks/logs/audit.jsonl
Para filtrar solo las decisiones denegadas:
jq 'select(.event=="policyDeny")' .github/hooks/logs/audit.jsonl
jq 'select(.event=="policyDeny")' .github/hooks/logs/audit.jsonl
7. Implementar de forma segura en todos los equipos
Después de validar los enlaces en un único repositorio, impleméntelos gradualmente para evitar interrumpir los flujos de trabajo de desarrollo.
Elección de una estrategia de lanzamiento
Entre los enfoques comunes de lanzamiento se incluyen:
-
**Despliegue centrado en el registro (recomendado)**: empiece por registrar las solicitudes y el uso de la herramienta sin denegar la ejecución. Revise los registros durante un período de tiempo y, a continuación, introduzca reglas de denegación una vez que comprenda los patrones de uso comunes. -
**Despliegue por equipos**: implemente enlaces en un equipo o repositorio a la vez, recopile comentarios y expanda a equipos adicionales. -
**Implementación basada en** riesgos: comience con repositorios que controlan sistemas confidenciales o infraestructura de producción y, a continuación, expanda a repositorios de menor riesgo.
Comunicar expectativas
Antes de aplicar reglas de denegación, asegúrese de que los desarrolladores comprendan lo siguiente:
- Esos enlaces están activos en el repositorio
- Qué tipos de comandos se pueden bloquear
- Procedimiento para continuar si se deniega un comando
La comunicación clara reduce la confusión y las solicitudes de soporte técnico.
Mantener las políticas mantenibles
A medida que evoluciona el uso:
- Almacene la configuración del enlace y los scripts en el control de versiones.
- Revise los registros de auditoría periódicamente para detectar nuevos patrones de riesgo.
- Actualice las reglas de denegación incrementalmente en lugar de agregar coincidencias amplias.
- Documente por qué existe cada regla de denegación, especialmente para las restricciones de alto impacto.
Maneja las excepciones con cuidado
Algunos equipos (por ejemplo, los equipos de infraestructura o plataforma) pueden requerir permisos más amplios. Para controlar esto de forma segura:
- Mantenga configuraciones de enlace independientes para distintos repositorios.
- Mantenga las excepciones estrechas y bien documentadas.
- Evite los desvíos locales ad hoc que minan la capacidad de auditoría.
Lectura adicional
Para resolver problemas de ganchos, consulte Uso de enlaces con agentes de GitHub Copilot.