# 예측 가능한 정책 규격을 준수하는 실행을 위해 Copilot CLI와 후크 사용

후크를 사용하여 사용자 프롬프트를 기록하고 리포지토리에서 실행할 수 있는 도구를 코파일럿 CLI 제어하므로 팀은 조직의 보안 및 규정 준수 요구 사항 내에서 안전하게 자동화할 수 있습니다.

이 자습서는 코파일럿 CLI를 사용하는 개발자를 지원하는 DevOps-엔지니어, 플랫폼-팀 및 엔지니어링-리더를 위한 것입니다.

후크는 세션 중에 특정 지점에서 실행되는 사용자 지정 스크립트입니다 코파일럿 CLI . 프롬프트 및 도구 호출, 감사에 대한 로그 정보 및 특정 명령의 실행을 차단할 수도 있습니다.

다음과 같은 리포지토리 범위 후크를 구성합니다.

* 프롬프트 및 도구 사용에 대한 가시성을 제공합니다.
* 실행 전에 위험 수준이 높은 명령 패턴을 차단합니다.
* 개발자가 명확한 메시징을 사용하여 조직 정책을 이해할 수 있도록 지원합니다.

## 필수 조건

* 셸 스크립팅에 대한 숙지(Bash 또는 PowerShell)
* JSON 구성 파일에 대한 기본 이해
* 코파일럿 CLI가 사용되는 리포지토리에 대한 액세스
* `jq` 설치됨(Bash 예제의 경우)

## 1. 조직 정책 정의

후크 스크립트를 작성하기 전에 자동으로 허용되는 작업과 사용자 검토가 필요한 작업을 결정합니다.

명확한 정책을 사용하면 위험을 줄이면서 과잉 차단을 방지할 수 있습니다.

### 항상 검토가 필요한 명령 식별

우선 코파일럿 CLI에 의해 절대 자동 실행되어서는 안 되는 패턴을 식별합니다. 일반적인 예는 다음과 같습니다.

* **권한 에스컬레이션**: `sudo`, `su``runas`
* **파괴적인 시스템 작업**: `rm -rf /`, `mkfs`, `dd``format`
* **다운로드 및 실행 패턴**: `curl ... | bash`, `wget ... | sh`, PowerShell `iex (irm ...)`

이러한 명령은 의도치 않게 실행되는 경우 되돌릴 수 없는 영향을 미칠 수 있습니다.

### 로깅할 내용 결정

후크를 사용하는 경우 코파일럿 CLI와 같은 사용자가 제출한 프롬프트 및 코파일럿 CLI 실행을 시도하는 도구를 포함하여 리포지토리에서 사용되는 방법에 대한 정보를 캡처할 수 있습니다.

최소한 대부분의 조직에서는 다음을 기록합니다.

* 타임스탬프 및 리포지토리 경로
* 프롬프트 텍스트(또는 수정된 양식)
* 도구 이름 및 도구 인수
* 모든 정책 결정(예: 거부된 명령 및 그 이유)

비밀 또는 자격 증명을 로깅하지 않습니다. 프롬프트 또는 명령에 중요한 데이터가 포함될 수 있는 경우 로그를 작성하기 전에 수정을 적용합니다.

이 자습서에서는 간단한 설명 예제로 로컬 `.github/hooks/logs` 디렉터리를 사용합니다. 이러한 로그 파일은 **리포지토리에 커밋되지 않으며** 일반적으로 개발자의 컴퓨터에만 저장됩니다.

프로덕션 환경에서는 많은 조직에서 로컬로 로그를 작성하는 대신 중앙 집중식 로깅 또는 관찰 시스템에 후크 이벤트를 전달합니다. 이를 통해 팀은 리포지토리 및 사용자 간에 일관된 편집, access 제어, 보존 정책 및 모니터링을 적용할 수 있습니다.

### 관련자와 일치

정책을 적용하기 전에 다음을 사용하여 검토합니다.

* 보안 또는 규정 준수 팀, 위험 경계 확인
* 더 광범위한 권한이 필요할 수 있는 플랫폼 또는 인프라 팀
* 개발 팀은 차단되는 내용과 이유를 이해합니다.

명확한 기대치를 통해 정책 적용을 더 쉽게 채택하고 유지 관리할 수 있습니다.

## 2. 리포지토리 후크 파일 설정

이 튜토리얼 전체에서 **저장소 범위 후크**를 사용하며, 해당 후크는 저장소 내 `.github/hooks/`에 저장됩니다. 이러한 후크는 이 리포지토리 내에서 코파일럿 CLI이 실행될 때마다 적용됩니다.

> \[!NOTE]

```
          Copilot 에이전트는 리포지토리에서 `.github/hooks/*.json` 후크 구성 파일을 로드합니다. 후크는 동기적으로 실행되며 실행을 차단할 수 있습니다.
```

### 디렉터리 구조 만들기

리포지토리 루트에서 후크 구성, 스크립트 및 로그에 대한 디렉터리를 만듭니다.

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

로컬 감사 로그가 커밋되지 않도록 .gitignore에 `.github/hooks/logs/` 추가합니다.

```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
```

### 후크 구성 파일 만들기

```
          `.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
```

이 시점에서 코파일럿 CLI 후크 스크립트를 아직 만들지 않았더라도 후크 구성을 검색할 수 있습니다.

## 3. 세션 시작 시 정책 배너 추가

새로운 `sessionStart`세션이 시작되거나 기존 세션이 재개될 때마다 배너가 표시되도록 설정하려면, 코파일럿 CLI 훅을 구성하여 활용하십시오. 이렇게 하면 개발자에게 조직 정책이 활성 상태임을 분명히 할 수 있습니다.

```
          `sessionStart` 후크는 현재 작업 디렉터리 및 초기 프롬프트와 같은 컨텍스트 정보를 받습니다. 이 후크의 모든 출력은 무시 코파일럿 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` 환경에서 프롬프트를 제출하는 시점을 추적하여 기록하려면, 코파일럿 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
```

이 시점에서 이 리포지토리에 제출된 코파일럿 CLI 프롬프트는 감사를 위해 기록됩니다.

## 5. 다음을 사용하여 정책 적용 `preToolUse`

```
          `preToolUse` 후크를 사용하여 **실행하기 전에** 도구 호출을 평가합니다. 이 후크는 아무 작업도 수행하지 않고 실행을 허용하거나 구조적 응답을 반환하여 실행을 거부할 수 있습니다.
```

###

```
          `preToolUse` 입력 이해

          `preToolUse` 후크 입력에는 다음이 포함됩니다.
```

* `toolName`: 실행하려는 도구 코파일럿 CLI (예: `bash`)입니다.
* `toolArgs`: 해당 도구의 인수를 포함하는 **JSON 문자열**

  ```
          `toolArgs`은 JSON 문자열이므로, 스크립트는 `command`와 같은 필드를 읽기 전에 해당 문자열을 파싱해야 합니다.
  ```

> \[!IMPORTANT]
> 도구 인수 및 명령에는 API 토큰, 암호 또는 기타 자격 증명과 같은 중요한 정보가 포함될 수 있습니다. 이 데이터를 로깅하기 전에 수정을 적용하고 조직의 보안 정책을 따릅니다. 중요하지 않은 메타데이터(도구 이름, 타임스탬프, 정책 결정)만 로깅하고 적절한 access 제어 및 보존 정책을 사용하여 감사 이벤트를 중앙 집중식으로 보호된 로깅 시스템으로 전송하는 것이 좋습니다.

### 정책 스크립트 만들기

다음으로, 정책 스크립트를 만듭니다. 이 예제:

* 시도된 모든 도구 사용을 기록합니다.
* bash 명령에만 거부 규칙을 적용합니다.
* 권한 상승, 파괴적인 작업 및 다운로드 및 실행 명령과 같은 위험 수준이 높은 패턴을 차단합니다.

거부 흐름의 유효성을 안전하게 검사할 수 있도록 스크립트에는 무해한 테스트 명령을 차단하는 임시 데모 규칙도 포함됩니다. 후크가 예상대로 작동하는지 확인한 후 데모 규칙을 제거하고 조직의 정책을 반영하는 패턴으로 바꿉니다.

#### 예제 스크립트(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. 리포지토리에서 엔드투엔드 테스트

구성 파일 및 스크립트를 만든 후에는 이 리포지토리에서 사용할 코파일럿 CLI 때 후크가 예상대로 실행되는지 확인합니다.

### 후크 구성 파일의 유효성 검사

후크 구성 파일이 유효한 JSON인지 확인합니다.

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

### 스크립트 권한 확인(Unix 기반 시스템)

macOS 및 Linux에서 Bash 스크립트가 실행 가능한지 확인합니다.

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

### 기본 세션 실행

리포지토리에서 새 코파일럿 CLI 세션을 시작합니다.

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

예상 결과:

* 정책 배너(원본 `sessionStart`)가 표시됩니다.
* 새 항목이 `.github/hooks/logs/audit.jsonl`(`userPromptSubmitted`)에 추가됩니다.

### 트리거 도구 사용 및 로깅 확인

도구를 사용하도록 하는 코파일럿 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"
```

예상 결과:

* 코파일럿 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. 팀 전체에 안전하게 배포

단일 리포지토리에서 후크의 유효성을 검사한 후 개발 워크플로를 방해하지 않도록 점진적으로 배포합니다.

### 출시 전략 선택

일반적인 출시 방법은 다음과 같습니다.

* **로깅 우선 롤아웃(권장)**: 실행을 거부하지 않고 프롬프트 및 도구 사용을 로깅하여 시작합니다. 일정 기간 동안 로그를 검토한 다음 일반적인 사용 패턴을 이해하면 거부 규칙을 도입합니다.
* **팀별 롤아웃**: 한 번에 한 팀 또는 리포지토리에 후크를 배포하고, 피드백을 수집한 다음, 추가 팀으로 확장합니다.
* **위험 기반 롤아웃**: 중요한 시스템 또는 프로덕션 인프라를 처리하는 리포지토리로 시작한 다음, 위험 수준이 낮은 리포지토리로 확장합니다.

### 기대치 전달

거부 규칙을 적용하기 전에 개발자가 다음을 이해해야 합니다.

* 해당 후크는 리포지토리에서 활성화됩니다.
* 차단할 수 있는 명령 유형
* 명령이 거부된 경우 진행하는 방법

명확한 통신은 혼동을 줄이고 요청을 지원합니다.

### 정책을 유지 관리할 수 있도록 유지

사용량이 발전함에 따라 다음을 수행합니다.

* 버전 제어에 후크 구성 및 스크립트를 저장합니다.
* 감사 로그를 주기적으로 검토하여 새 위험 패턴을 검색합니다.
* 광범위한 일치 항목을 추가하는 대신 거부 규칙을 증분 방식으로 업데이트합니다.
* 특히 영향이 큰 제한에 대해 각 거부 규칙이 존재하는 이유를 문서화합니다.

### 예외를 신중하게 처리

일부 팀(예: 인프라 또는 플랫폼 팀)에는 더 광범위한 권한이 필요할 수 있습니다. 이 작업을 안전하게 처리하려면 다음을 수행합니다.

* 다른 리포지토리에 대해 별도의 후크 구성을 유지 관리합니다.
* 예외를 좁고 잘 문서화된 상태로 유지합니다.
* 감사 가능성을 저해하는 임시 로컬 바이패스를 방지합니다.

## 추가 읽기

후크 문제를 해결하려면 [후크를 사용하여 에이전트 워크플로 사용자 지정](/ko/copilot/how-tos/use-copilot-agents/cloud-agent/use-hooks#troubleshooting)을 참조하세요.