{"meta":{"title":"예측 가능한 정책 규격을 준수하는 실행을 위해 Copilot CLI와 후크 사용","intro":"후크를 사용하여 사용자 프롬프트를 기록하고 리포지토리에서 실행할 수 있는 도구를 Copilot CLI 제어하므로 팀은 조직의 보안 및 규정 준수 요구 사항 내에서 안전하게 자동화할 수 있습니다.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ko/copilot","title":"GitHub Copilot"},{"href":"/ko/copilot/tutorials","title":"자습서"},{"href":"/ko/copilot/tutorials/copilot-cli-hooks","title":"Copilot CLI에서 후크 사용"}],"documentType":"article"},"body":"# 예측 가능한 정책 규격을 준수하는 실행을 위해 Copilot CLI와 후크 사용\n\n후크를 사용하여 사용자 프롬프트를 기록하고 리포지토리에서 실행할 수 있는 도구를 Copilot CLI 제어하므로 팀은 조직의 보안 및 규정 준수 요구 사항 내에서 안전하게 자동화할 수 있습니다.\n\n이 자습서는 Copilot CLI를 사용하는 개발자를 지원하는 DevOps-엔지니어, 플랫폼-팀 및 엔지니어링-리더를 위한 것입니다.\n\n후크는 세션 중에 특정 지점에서 실행되는 사용자 지정 스크립트입니다 Copilot CLI . 프롬프트 및 도구 호출, 감사에 대한 로그 정보 및 특정 명령의 실행을 차단할 수도 있습니다.\n\n다음과 같은 리포지토리 범위 후크를 구성합니다.\n\n* 프롬프트 및 도구 사용에 대한 가시성을 제공합니다.\n* 실행 전에 위험 수준이 높은 명령 패턴을 차단합니다.\n* 개발자가 명확한 메시징을 사용하여 조직 정책을 이해할 수 있도록 지원합니다.\n\n## 필수 조건\n\n* 셸 스크립팅(Bash 또는 PowerShell)에 대해 잘 알고 있습니다.\n* JSON 구성 파일에 대한 기본 이해입니다.\n* Copilot CLI이 사용되는 리포지토리에 대한 액세스\n* Bash 예제의 경우 다음 `jq` 을 설치해야 합니다.\n* PowerShell 예제: PowerShell 7.0 이상을 설치해야 합니다.\n\n## 1. 조직 정책 정의\n\n후크 스크립트를 작성하기 전에 자동으로 허용되는 작업과 사용자 검토가 필요한 작업을 결정합니다.\n\n명확한 정책을 사용하면 위험을 줄이면서 과잉 차단을 방지할 수 있습니다.\n\n### 항상 검토가 필요한 명령 식별\n\n우선 Copilot CLI에 의해 절대 자동 실행되어서는 안 되는 패턴을 식별합니다. 일반적인 예는 다음과 같습니다.\n\n* **권한 에스컬레이션**: `sudo`, `su``runas`\n* **파괴적인 시스템 작업**: `rm -rf /`, `mkfs`, `dd``format`\n* **다운로드 및 실행 패턴**: `curl ... | bash`, `wget ... | sh`, PowerShell `iex (irm ...)`\n\n이러한 명령은 의도치 않게 실행되는 경우 되돌릴 수 없는 영향을 미칠 수 있습니다.\n\n### 로깅할 내용 결정\n\n후크를 사용하는 경우 Copilot CLI와 같은 사용자가 제출한 프롬프트 및 Copilot CLI 실행을 시도하는 도구를 포함하여 리포지토리에서 사용되는 방법에 대한 정보를 캡처할 수 있습니다.\n\n최소한 대부분의 조직에서는 다음을 기록합니다.\n\n* 타임스탬프 및 리포지토리 경로\n* 프롬프트 텍스트(또는 수정된 양식)\n* 도구 이름 및 도구 인수\n* 모든 정책 결정(예: 거부된 명령 및 그 이유)\n\n비밀 또는 자격 증명을 로깅하지 않습니다. 프롬프트 또는 명령에 중요한 데이터가 포함될 수 있는 경우 로그를 작성하기 전에 수정을 적용합니다.\n\n이 자습서에서는 간단한 설명 예제로 로컬 `.github/hooks/logs` 디렉터리를 사용합니다. 이러한 로그 파일은 **리포지토리에 커밋되지 않으며** 일반적으로 개발자의 컴퓨터에만 저장됩니다.\n\n프로덕션 환경에서는 많은 조직에서 로컬로 로그를 작성하는 대신 중앙 집중식 로깅 또는 관찰 시스템에 후크 이벤트를 전달합니다. 이를 통해 팀은 리포지토리 및 사용자 간에 일관된 편집, access 제어, 보존 정책 및 모니터링을 적용할 수 있습니다.\n\n### 관련자와 일치\n\n정책을 적용하기 전에 다음을 사용하여 검토합니다.\n\n* 보안 또는 규정 준수 팀, 위험 경계 확인\n* 더 광범위한 권한이 필요할 수 있는 플랫폼 또는 인프라 팀\n* 개발 팀은 차단되는 내용과 이유를 이해합니다.\n\n명확한 기대치를 통해 정책 적용을 더 쉽게 채택하고 유지 관리할 수 있습니다.\n\n## 2. 리포지토리 후크 파일 설정\n\n이 튜토리얼 전체에서 **저장소 범위 후크**를 사용하며, 해당 후크는 저장소 내 `.github/hooks/`에 저장됩니다. 이러한 후크는 이 리포지토리 내에서 Copilot CLI이 실행될 때마다 적용됩니다.\n\n> \\[!NOTE]\n> Copilot 에이전트는 리포지토리의 파일에서 `.github/hooks/*.json` 후크 구성을 로드합니다. 후크는 동기적으로 실행되며 실행을 차단할 수 있습니다.\n\n### 디렉터리 구조 만들기\n\n리포지토리 루트에서 후크 구성, 스크립트 및 로그에 대한 디렉터리를 만듭니다.\n\n```bash copy\nmkdir -p .github/hooks/scripts\nmkdir -p .github/hooks/logs\n```\n\n로컬 감사 로그가 커밋되지 않도록 .gitignore에 `.github/hooks/logs/` 추가합니다.\n\n```bash copy\necho \".github/hooks/logs/\" >> .gitignore\n```\n\n이 자습서에서는 다음 구조를 사용합니다.\n\n```text\n.github/\n└── hooks/\n    ├── copilot-cli-policy.json\n    ├── logs/\n    │   └── audit.jsonl\n    └── scripts/\n        ├── session-banner.sh\n        ├── session-banner.ps1\n        ├── log-prompt.sh\n        ├── log-prompt.ps1\n        ├── pre-tool-policy.sh\n        └── pre-tool-policy.ps1\n```\n\n> \\[!NOTE]\n> 이 자습서에서는 Windows, Linux 및 macOS에서 사용할 수 있는 이식 가능한 후크 구성 및 스크립트를 만들려고 합니다. 따라서 `scripts` 디렉터리에는 Bash 및 PowerShell 스크립트가 모두 포함되며, 후크 구성 파일에는 `bash` 및 `powershell` 항목이 포함됩니다. CLI는 운영 체제에 따라 적절한 항목을 사용합니다.\n\n### 후크 구성 파일 만들기\n\n`.github/hooks/copilot-cli-policy.json`에 후크 구성 파일을 생성하세요.\n\n이 파일은 실행되는 후크, 실행할 때 및 실행하는 스크립트를 정의합니다.\n\n```json copy\n{\n  \"version\": 1,\n  \"hooks\": {\n    \"sessionStart\": [\n      {\n        \"type\": \"command\",\n        \"bash\": \"./scripts/session-banner.sh\",\n        \"powershell\": \"./scripts/session-banner.ps1\",\n        \"cwd\": \".github/hooks\",\n        \"timeoutSec\": 10\n      }\n    ],\n    \"userPromptSubmitted\": [\n      {\n        \"type\": \"command\",\n        \"bash\": \"./scripts/log-prompt.sh\",\n        \"powershell\": \"./scripts/log-prompt.ps1\",\n        \"cwd\": \".github/hooks\",\n        \"timeoutSec\": 10\n      }\n    ],\n    \"preToolUse\": [\n      {\n        \"type\": \"command\",\n        \"bash\": \"./scripts/pre-tool-policy.sh\",\n        \"powershell\": \"./scripts/pre-tool-policy.ps1\",\n        \"cwd\": \".github/hooks\",\n        \"timeoutSec\": 15\n      }\n    ]\n  }\n}\n```\n\n### 이 구성이 수행하는 작업 이해\n\n이 구성은 세 가지 후크를 설정합니다.\n\n* `sessionStart`: 새 에이전트 세션이 시작되거나 다시 시작될 때 정보 메시지를 표시합니다.\n* `userPromptSubmitted`: 사용자가 프롬프트를 제출할 때마다 실행됩니다.\n* `preToolUse`: 도구가 실행되기 전에 실행되며 실행을 명시적으로 허용하거나 거부할 수 있습니다.\n\n### 후크 구성 커밋 및 공유\n\n후크 구성을 공동 작업자와 공유할 준비가 되면(예: 끌어오기 요청 또는 테스트 리포지토리에서) 후크 구성 및 스크립트를 커밋합니다. 로컬 감사 로그를 커밋하지 마세요.\n\n```bash copy\ngit add .github/hooks/copilot-cli-policy.json .github/hooks/scripts\ngit commit -m \"Add Copilot CLI hook configuration\"\ngit push\n```\n\n이 시점에서 Copilot CLI 후크 스크립트를 아직 만들지 않았더라도 후크 구성을 검색할 수 있습니다.\n\n## 3. 세션 시작 시 정책 배너 추가\n\n새로운 `sessionStart`세션이 시작되거나 기존 세션이 재개될 때마다 배너가 표시되도록 설정하려면, Copilot CLI 훅을 구성하여 활용하십시오. 이렇게 하면 개발자에게 조직 정책이 활성 상태임을 분명히 할 수 있습니다.\n\n`sessionStart` 후크는 현재 작업 디렉터리 및 초기 프롬프트와 같은 컨텍스트 정보를 받습니다. 이 후크의 모든 출력은 무시 Copilot CLI되므로 정보 메시지에 적합합니다.\n\n### 세션 배너 스크립트 만들기(Bash)\n\n`.github/hooks/scripts/session-banner.sh` 만듭니다.\n\n```bash copy\n#!/bin/bash\nset -euo pipefail\n\ncat << 'EOF'\nCOPILOT CLI POLICY ACTIVE\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Prompts and tool use may be logged for auditing\n• High-risk commands may be blocked automatically\n• If something is blocked, follow the guidance shown\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nEOF\nexit 0\n```\n\n### 세션 배너 스크립트 만들기(PowerShell)\n\n`.github/hooks/scripts/session-banner.ps1` 만듭니다.\n\n```powershell copy\n$ErrorActionPreference = \"Stop\"\n\nWrite-Host @\"\nCOPILOT CLI POLICY ACTIVE\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Prompts and tool use may be logged for auditing\n• High-risk commands may be blocked automatically\n• If something is blocked, follow the guidance shown\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\"@\nexit 0\n```\n\n### 세션 배너 테스트\n\n배너 스크립트를 직접 테스트할 수 있습니다.\n\n```bash\n.github/hooks/scripts/session-banner.sh\n# or, for PowerShell\n.github/hooks/scripts/session-banner.ps1\n```\n\n두 스크립트 중 하나를 실행하면 터미널에 정책 배너가 표시됩니다.\n\n## 4. 감사에 대한 로그 프롬프트\n\n사용자가 `userPromptSubmitted` 환경에서 프롬프트를 제출하는 시점을 추적하여 기록하려면, Copilot CLI훅을 시스템에 구성하여 사용하십시오. 이 후크는 도구를 호출하기 전에 프롬프트가 전송될 때마다 실행됩니다.\n\n후크는 타임스탬프, 현재 작업 디렉터리 및 전체 프롬프트 텍스트를 포함하는 구조화된 JSON 입력을 받습니다. 이 후크의 출력은 무시됩니다.\n\n> \\[!IMPORTANT]\n> 프롬프트에 중요한 정보가 포함될 수 있습니다. 이 데이터를 로깅할 때 수정을 적용하고 조직의 데이터 처리 및 보존 정책을 따릅니다.\n\n### 프롬프트 로깅 스크립트 만들기(Bash)\n\n`.github/hooks/scripts/log-prompt.sh` 만듭니다.\n\n```bash copy\n#!/bin/bash\nset -euo pipefail\n\nINPUT=\"$(cat)\"\n\nTIMESTAMP_MS=\"$(echo \"$INPUT\" | jq -r '.timestamp // empty')\"\nCWD=\"$(echo \"$INPUT\" | jq -r '.cwd // empty')\"\n\n# This example logs only metadata, not the full prompt, to avoid storing\n# potentially sensitive data. Adjust to match your organization’s needs.\nLOG_DIR=\".github/hooks/logs\"\nmkdir -p \"$LOG_DIR\"\nchmod 700 \"$LOG_DIR\"\n\njq -n \\\n  --arg ts \"$TIMESTAMP_MS\" \\\n  --arg cwd \"$CWD\" \\\n  '{event:\"userPromptSubmitted\", timestampMs:$ts, cwd:$cwd}' \\\n  >> \"$LOG_DIR/audit.jsonl\"\n\nexit 0\n```\n\n### 프롬프트 로깅 스크립트 만들기(PowerShell)\n\n`.github/hooks/scripts/log-prompt.ps1` 만듭니다.\n\n```powershell copy\n$ErrorActionPreference = \"Stop\"\n\n$inputObj = [Console]::In.ReadToEnd() | ConvertFrom-Json\n\n$timestampMs = $inputObj.timestamp\n$cwd = $inputObj.cwd\n$prompt = $inputObj.prompt\n\n# Optional example redaction. Adjust to match your organization’s needs.\n$redactedPrompt = $prompt -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]'\n\n$logDir = \".github/hooks/logs\"\nif (-not (Test-Path $logDir)) {\n  New-Item -ItemType Directory -Path $logDir -Force | Out-Null\n}\n\n$logEntry = @{\n  event       = \"userPromptSubmitted\"\n  timestampMs = $timestampMs\n  cwd         = $cwd\n  prompt      = $redactedPrompt\n} | ConvertTo-Json -Compress\n\nAdd-Content -Path \"$logDir/audit.jsonl\" -Value $logEntry\nexit 0\n```\n\n### 프롬프트 로깅 스크립트 테스트\n\n예제 입력을 파이핑하여 스크립트를 직접 테스트할 수 있습니다.\n\n```bash\necho '{\"timestamp\":1704614500000,\"cwd\":\"/repo\",\"prompt\":\"List all branches\"}' \\\n  | .github/hooks/scripts/log-prompt.sh\n# or, for PowerShell\necho '{\"timestamp\":1704614500000,\"cwd\":\"/repo\",\"prompt\":\"List all branches\"}' |\n  .github/hooks/scripts/log-prompt.ps1\n```\n\n스크립트를 실행한 후 `.github/hooks/logs/audit.jsonl` 새 로그 항목을 확인합니다.\n\n```bash copy\ncat .github/hooks/logs/audit.jsonl\n```\n\n이 시점에서 이 리포지토리에 제출된 Copilot CLI 프롬프트는 감사를 위해 기록됩니다.\n\n## 5. 다음을 사용하여 정책 적용 `preToolUse`\n\n`preToolUse` 후크를 사용하여 **실행하기 전에** 도구 호출을 평가합니다. 이 후크는 아무 작업도 수행하지 않고 실행을 허용하거나 구조적 응답을 반환하여 실행을 거부할 수 있습니다.\n\n### `preToolUse` 입력 이해\n\n`preToolUse` 후크 입력에는 다음이 포함됩니다.\n\n* `toolName`: 실행하려는 도구 Copilot CLI (예: `bash`)입니다.\n* `toolArgs`: 해당 도구의 인수를 포함하는 **JSON 문자열**\n\n`toolArgs`은 JSON 문자열이므로, 스크립트는 `command`와 같은 필드를 읽기 전에 해당 문자열을 파싱해야 합니다.\n\n> \\[!IMPORTANT]\n> 도구 인수 및 명령에는 API 토큰, 암호 또는 기타 자격 증명과 같은 중요한 정보가 포함될 수 있습니다. 이 데이터를 로깅하기 전에 수정을 적용하고 조직의 보안 정책을 따릅니다. 중요하지 않은 메타데이터(도구 이름, 타임스탬프, 정책 결정)만 로깅하고 적절한 access 제어 및 보존 정책을 사용하여 감사 이벤트를 중앙 집중식으로 보호된 로깅 시스템으로 전송하는 것이 좋습니다.\n\n### 정책 스크립트 만들기\n\n다음으로, 정책 스크립트를 만듭니다. 이 예제:\n\n* 시도된 모든 도구 사용을 기록합니다.\n* bash 명령에만 거부 규칙을 적용합니다.\n* 권한 상승, 파괴적인 작업 및 다운로드 및 실행 명령과 같은 위험 수준이 높은 패턴을 차단합니다.\n\n거부 흐름의 유효성을 안전하게 검사할 수 있도록 스크립트에는 무해한 테스트 명령을 차단하는 임시 데모 규칙도 포함됩니다. 후크가 예상대로 작동하는지 확인한 후 데모 규칙을 제거하고 조직의 정책을 반영하는 패턴으로 바꿉니다.\n\n#### 예제 스크립트(Bash)\n\n`.github/hooks/scripts/pre-tool-policy.sh` 만듭니다.\n\n```bash copy\n#!/bin/bash\nset -euo pipefail\n\nINPUT=\"$(cat)\"\n\nTOOL_NAME=\"$(echo \"$INPUT\" | jq -r '.toolName // empty')\"\nTOOL_ARGS_RAW=\"$(echo \"$INPUT\" | jq -r '.toolArgs // empty')\"  # JSON string\n\nLOG_DIR=\".github/hooks/logs\"\nmkdir -p \"$LOG_DIR\"\n\n# Example redaction logic.\n# GitHub does not currently provide built-in secret redaction for hooks.\n# This example shows one possible approach; many organizations prefer to\n# forward events to a centralized logging system that handles redaction.\n# Redact sensitive patterns before logging.\n# Adjust these patterns to match your organization's needs.\nREDACTED_TOOL_ARGS=\"$(echo \"$TOOL_ARGS_RAW\" | \\\n  sed -E 's/ghp_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/gho_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/ghu_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/ghs_[A-Za-z0-9\\._\\-]{20,}/[REDACTED_TOKEN]/g' | \\\n  sed -E 's/Bearer [A-Za-z0-9_\\-\\.]+/Bearer [REDACTED]/g' | \\\n  sed -E 's/--password[= ][^ ]+/--password=[REDACTED]/g' | \\\n  sed -E 's/--token[= ][^ ]+/--token=[REDACTED]/g')\"\n\n# Log attempted tool use with redacted toolArgs.\njq -n \\\n  --arg tool \"$TOOL_NAME\" \\\n  --arg toolArgs \"$REDACTED_TOOL_ARGS\" \\\n  '{event:\"preToolUse\", toolName:$tool, toolArgs:$toolArgs}' \\\n  >> \"$LOG_DIR/audit.jsonl\"\n\n# Only enforce command rules for bash.\nif [ \"$TOOL_NAME\" != \"bash\" ]; then\n  exit 0\nfi\n\n# Parse toolArgs JSON string.\n# If toolArgs isn't valid JSON for some reason, allow (and rely on logs).\nif ! echo \"$TOOL_ARGS_RAW\" | jq -e . >/dev/null 2>&1; then\n  exit 0\nfi\n\nCOMMAND=\"$(echo \"$TOOL_ARGS_RAW\" | jq -r '.command // empty')\"\n\n# ---------------------------------------------------------------------------\n# Demo-only deny rule for safe testing.\n# This blocks a harmless test command so you can validate the deny flow.\n# Remove this rule after confirming your hooks work as expected.\n# ---------------------------------------------------------------------------\nif echo \"$COMMAND\" | grep -q \"COPILOT_HOOKS_DENY_DEMO\"; then\n  deny \"Blocked demo command (test rule). Remove this rule after validating hooks.\"\nfi\n\ndeny() {\n  local reason=\"$1\"\n\n  # Redact sensitive patterns from command before logging.\n  local redacted_cmd=\"$(echo \"$COMMAND\" | \\\n    sed -E 's/ghp_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/gho_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/ghu_[A-Za-z0-9]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/ghs_[A-Za-z0-9\\.\\-_]{20,}/[REDACTED_TOKEN]/g' | \\\n    sed -E 's/Bearer [A-Za-z0-9_\\-\\.]+/Bearer [REDACTED]/g' | \\\n    sed -E 's/--password[= ][^ ]+/--password=[REDACTED]/g' | \\\n    sed -E 's/--token[= ][^ ]+/--token=[REDACTED]/g')\"\n\n  # Log the denial decision with redacted command.\n  jq -n \\\n    --arg cmd \"$redacted_cmd\" \\\n    --arg r \"$reason\" \\\n    '{event:\"policyDeny\", toolName:\"bash\", command:$cmd, reason:$r}' \\\n    >> \"$LOG_DIR/audit.jsonl\"\n\n  # Return a denial response.\n  jq -n \\\n    --arg r \"$reason\" \\\n    '{permissionDecision:\"deny\", permissionDecisionReason:$r}'\n\n  exit 0\n}\n\n# Privilege escalation\nif echo \"$COMMAND\" | grep -qE '\\b(sudo|su|runas)\\b'; then\n  deny \"Privilege escalation requires manual approval.\"\nfi\n\n# Destructive filesystem operations targeting root\nif echo \"$COMMAND\" | grep -qE 'rm\\s+-rf\\s*/($|\\s)|rm\\s+.*-rf\\s*/($|\\s)'; then\n  deny \"Destructive operations targeting the filesystem root require manual approval.\"\nfi\n\n# System-level destructive operations\nif echo \"$COMMAND\" | grep -qE '\\b(mkfs|dd|format)\\b'; then\n  deny \"System-level destructive operations are not allowed via automated execution.\"\nfi\n\n# Download-and-execute patterns\nif echo \"$COMMAND\" | grep -qE 'curl.*\\|\\s*(bash|sh)|wget.*\\|\\s*(bash|sh)'; then\n  deny \"Download-and-execute patterns require manual approval.\"\nfi\n\n# Allow by default\nexit 0\n```\n\n#### 정책 스크립트 만들기(PowerShell)\n\n`.github/hooks/scripts/pre-tool-policy.ps1` 만듭니다.\n\n```powershell copy\n$ErrorActionPreference = \"Stop\"\n\n$inputObj = [Console]::In.ReadToEnd() | ConvertFrom-Json\n$toolName = $inputObj.toolName\n$toolArgsRaw = $inputObj.toolArgs  # JSON string\n\n$logDir = \".github/hooks/logs\"\nif (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }\n\n# Example redaction logic.\n# GitHub does not currently provide built-in secret redaction for hooks.\n# This example shows one possible approach; many organizations prefer to\n# forward events to a centralized logging system that handles redaction.\n# Redact sensitive patterns before logging.\n# Adjust these patterns to match your organization's needs.\n$redactedToolArgs = $toolArgsRaw `\n  -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'gho_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'ghu_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'ghs_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n  -replace 'Bearer [A-Za-z0-9_\\-\\.]+', 'Bearer [REDACTED]' `\n  -replace '--password[= ][^ ]+', '--password=[REDACTED]' `\n  -replace '--token[= ][^ ]+', '--token=[REDACTED]'\n\n# Log attempted tool use with redacted toolArgs.\n(@{\n  event    = \"preToolUse\"\n  toolName = $toolName\n  toolArgs = $redactedToolArgs\n} | ConvertTo-Json -Compress) | Add-Content -Path \"$logDir/audit.jsonl\"\n\nif ($toolName -ne \"bash\") { exit 0 }\n\n# Parse toolArgs JSON string.\n$toolArgs = $null\ntry { $toolArgs = $toolArgsRaw | ConvertFrom-Json } catch { exit 0 }\n\n$command = $toolArgs.command\n\n# ---------------------------------------------------------------------------\n# Demo-only deny rule for safe testing.\n# This blocks a harmless test command so you can validate the deny flow.\n# Remove this rule after confirming your hooks work as expected.\n# ---------------------------------------------------------------------------\nif ($command -match 'COPILOT_HOOKS_DENY_DEMO') {\n  Deny \"Blocked demo command (test rule). Remove this rule after validating hooks.\"\n}\n\nfunction Deny([string]$reason) {\n  # Redact sensitive patterns from command before logging.\n  $redactedCommand = $command `\n    -replace 'ghp_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'gho_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'ghu_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'ghs_[A-Za-z0-9]{20,}', '[REDACTED_TOKEN]' `\n    -replace 'Bearer [A-Za-z0-9_\\-\\.]+', 'Bearer [REDACTED]' `\n    -replace '--password[= ][^ ]+', '--password=[REDACTED]' `\n    -replace '--token[= ][^ ]+', '--token=[REDACTED]'\n\n  # Log the denial decision with redacted command.\n  (@{\n    event    = \"policyDeny\"\n    toolName = \"bash\"\n    command  = $redactedCommand\n    reason   = $reason\n  } | ConvertTo-Json -Compress) | Add-Content -Path \"$logDir/audit.jsonl\"\n\n  (@{\n    permissionDecision = \"deny\"\n    permissionDecisionReason = $reason\n  } | ConvertTo-Json -Compress)\n\n  exit 0\n}\n\nif ($command -match '\\b(sudo|su|runas)\\b') { Deny \"Privilege escalation requires manual approval.\" }\nif ($command -match 'rm\\s+-rf\\s*/(\\s|$)|rm\\s+.*-rf\\s*/(\\s|$)') { Deny \"Destructive operations targeting the filesystem root require manual approval.\" }\nif ($command -match '\\b(mkfs|dd|format)\\b') { Deny \"System-level destructive operations are not allowed via automated execution.\" }\nif ($command -match 'curl.*\\|\\s*(bash|sh)|wget.*\\|\\s*(bash|sh)') { Deny \"Download-and-execute patterns require manual approval.\" }\n\nexit 0\n```\n\n### 정책 스크립트 테스트\n\n예제 `preToolUse` 입력을 파이핑하여 스크립트를 테스트할 수 있습니다.\n\n허용 예제:\n\n```bash\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"git status\\\"}\"}' \\\n  | .github/hooks/scripts/pre-tool-policy.sh\n# or, for PowerShell\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"git status\\\"}\"}' |\n  .github/hooks/scripts/pre-tool-policy.ps1\n```\n\n거부 예제:\n\n```bash\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"sudo rm -rf /\\\"}\"}' \\\n  | .github/hooks/scripts/pre-tool-policy.sh\n# or, for PowerShell\necho '{\"toolName\":\"bash\",\"toolArgs\":\"{\\\"command\\\":\\\"sudo rm -rf /\\\"}\"}' |\n  .github/hooks/scripts/pre-tool-policy.ps1\n```\n\n거부 예제를 실행한 후 `.github/hooks/logs/audit.jsonl` 새 거부 로그 항목을 확인합니다.\n\n```json\n{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Privilege escalation requires manual approval.\"}\n```\n\n이 시점에서 위험 수준이 높은 `bash` 명령은 이 리포지토리에서 자동 실행에서 차단됩니다.\n\n## 6. 리포지토리에서 엔드투엔드 테스트\n\n구성 파일 및 스크립트를 만든 후에는 이 리포지토리에서 사용할 Copilot CLI 때 후크가 예상대로 실행되는지 확인합니다.\n\n### 후크 구성 파일의 유효성 검사\n\n후크 구성 파일이 유효한 JSON인지 확인합니다.\n\n```bash copy\njq '.' < .github/hooks/copilot-cli-policy.json\n```\n\n### 스크립트 권한 확인(Unix 기반 시스템)\n\nmacOS 및 Linux에서 Bash 스크립트가 실행 가능한지 확인합니다.\n\n```bash copy\nchmod +x .github/hooks/scripts/*.sh\n```\n\n### 기본 세션 실행\n\n리포지토리에서 새 Copilot CLI 세션을 시작합니다.\n\n```bash copy\ncopilot -p \"Show me the status of this repository\"\n```\n\n예상 결과:\n\n* 정책 배너(원본 `sessionStart`)가 표시됩니다.\n* 새 항목이 `.github/hooks/logs/audit.jsonl`(`userPromptSubmitted`)에 추가됩니다.\n\n### 트리거 도구 사용 및 로깅 확인\n\n도구를 사용하도록 하는 Copilot CLI 프롬프트를 실행합니다(예: bash).\n\n```bash copy\ncopilot -p \"Show me the last 5 git commits\"\n```\n\n예상 결과:\n\n* `preToolUse` 항목이 `.github/hooks/logs/audit.jsonl` 추가됩니다.\n* 도구 호출이 허용되면 실행이 정상적으로 진행됩니다.\n\n### 거부된 명령 테스트\n\n예제 정책 스크립트에는 문자열 `COPILOT_HOOKS_DENY_DEMO`을 포함하는 명령을 차단하는 임시 데모 규칙이 포함되어 있습니다. 이를 통해 파괴적인 명령을 실행하지 않고도 거부 흐름의 유효성을 안전하게 검사할 수 있습니다.\n\n거부된 명령을 트리거하는 프롬프트를 실행합니다.\n\n```bash copy\ncopilot -p \"Run a test command: echo COPILOT_HOOKS_DENY_DEMO\"\n```\n\n예상 결과:\n\n* Copilot CLI 명령을 실행하지 않습니다.\n* 훅은 명확한 이유로 거부 응답을 반환한다.\n* `policyDeny` 항목은 `.github/hooks/logs/audit.jsonl` 기록됩니다.\n\n거부 흐름이 올바르게 작동하는지 확인한 후 스크립트에서 데모 규칙을 제거하고 조직의 정책을 반영하는 거부 패턴으로 바꿉니다.\n\n### 감사 로그를 검사하세요\n\n최근 항목을 보려면 다음을 수행합니다.\n\n```bash copy\ntail -n 50 .github/hooks/logs/audit.jsonl\n```\n\n거부된 결정만 필터링하려면 다음을 수행합니다.\n\n```bash copy\njq 'select(.event==\"policyDeny\")' .github/hooks/logs/audit.jsonl\n```\n\n## 7. 팀 전체에 안전하게 배포\n\n단일 리포지토리에서 후크의 유효성을 검사한 후 개발 워크플로를 방해하지 않도록 점진적으로 배포합니다.\n\n### 출시 전략 선택\n\n일반적인 출시 방법은 다음과 같습니다.\n\n* **로깅 우선 롤아웃(권장)**: 실행을 거부하지 않고 프롬프트 및 도구 사용을 로깅하여 시작합니다. 일정 기간 동안 로그를 검토한 다음 일반적인 사용 패턴을 이해하면 거부 규칙을 도입합니다.\n* **팀별 롤아웃**: 한 번에 한 팀 또는 리포지토리에 후크를 배포하고, 피드백을 수집한 다음, 추가 팀으로 확장합니다.\n* **위험 기반 롤아웃**: 중요한 시스템 또는 프로덕션 인프라를 처리하는 리포지토리로 시작한 다음, 위험 수준이 낮은 리포지토리로 확장합니다.\n\n### 기대치 전달\n\n거부 규칙을 적용하기 전에 개발자가 다음을 이해해야 합니다.\n\n* 해당 후크는 리포지토리에서 활성화됩니다.\n* 차단할 수 있는 명령 유형\n* 명령이 거부된 경우 진행하는 방법\n\n명확한 통신은 혼동을 줄이고 요청을 지원합니다.\n\n### 정책을 유지 관리할 수 있도록 유지\n\n사용량이 발전함에 따라 다음을 수행합니다.\n\n* 버전 제어에 후크 구성 및 스크립트를 저장합니다.\n* 감사 로그를 주기적으로 검토하여 새 위험 패턴을 검색합니다.\n* 광범위한 일치 항목을 추가하는 대신 거부 규칙을 증분 방식으로 업데이트합니다.\n* 특히 영향이 큰 제한에 대해 각 거부 규칙이 존재하는 이유를 문서화합니다.\n\n### 예외를 신중하게 처리\n\n일부 팀(예: 인프라 또는 플랫폼 팀)에는 더 광범위한 권한이 필요할 수 있습니다. 이 작업을 안전하게 처리하려면 다음을 수행합니다.\n\n* 다른 리포지토리에 대해 별도의 후크 구성을 유지 관리합니다.\n* 예외를 좁고 잘 문서화된 상태로 유지합니다.\n* 감사 가능성을 저해하는 임시 로컬 바이패스를 방지합니다.\n\n## 추가 읽기\n\n후크 문제를 해결하려면 [후크를 사용하여 에이전트 워크플로 사용자 지정](/ko/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks#troubleshooting)을 참조하세요."}