How do you secure the GitHub Copilot CLI and cloud agent?

To secure GitHub Copilot's agents, install hooks that send each pending tool call to a policy service before the tool runs. The Copilot CLI and cloud coding agent accept HTTP hook entries and enforce tool-call denials. Arcjet decides each of those tool calls from the edge with the same policy you use for Claude Code, Cursor, and OpenAI Codex. Pair it with other controls where GitHub limits the hooks: prompt denials, model restrictions, and VS Code agent mode.

9 min read
In short: To secure GitHub Copilot's agents, install hooks that send each pending tool call to a policy service before the tool runs. The Copilot CLI and cloud coding agent accept HTTP hook entries and enforce tool-call denials. Arcjet decides each of those tool calls from the edge with the same policy you use for Claude Code, Cursor, and OpenAI Codex. Pair it with other controls where GitHub limits the hooks: prompt denials, model restrictions, and VS Code agent mode.

What does securing GitHub Copilot's agents involve?

To secure GitHub Copilot's agents, install hooks that send each pending tool call to a policy service before the tool runs. The Copilot CLI and the Copilot cloud coding agent accept HTTP hook entries, and on the CLI a root-owned policy file stops users disabling them. Copilot enforces tool-call denials but not prompt denials, and VS Code agent mode doesn't support HTTP hooks, so plan other controls for those gaps.

Copilot's agent surfaces don't only complete code. The Copilot CLI, agent mode in the editor, and the cloud coding agent run shell commands, create and edit files, fetch URLs, and call Model Context Protocol (MCP) servers. Copilot fires hooks, configuration entries that run at set points in its lifecycle. They include preToolUse before a tool runs, permissionRequest when Copilot asks for approval, and userPromptSubmitted when a prompt is submitted.

A hook can be an HTTP endpoint, and Arcjet is that endpoint. Arcjet denies a Copilot tool call before the tool runs and records the session, with no SDK, proxy, or code change. The same policy covers Claude Code, Cursor, and OpenAI Codex, so you don't maintain Copilot rules separately. The hook runs inside Copilot's own process: on the developer's machine for the CLI, and in GitHub's environment for the cloud agent. It isn't a gateway on the model call. For the model that all four supported agents share, see How do you secure AI coding agents?.

Which Copilot surfaces can hooks secure?

One Arcjet hook file serves both the Copilot CLI and the cloud coding agent. GitHub sets which surfaces accept HTTP hooks.

SurfaceWhere the hook file goesArcjet support
Copilot CLI

.github/hooks/*.json in the repository, or /etc/github-copilot/policy.d/arcjet.json for an administrator lock

Supported. The CLI honors HTTP entries as written.
Copilot cloud coding agent.github/hooks/*.json on the repository's default branch

Supported after the file is merged and the firewall allows decide.arcjet.com.

VS Code agent modeNot applicable

Not supported.

Agent hooks in VS Code

are in preview and don't support the HTTP hook type.

What does the Copilot hook file look like?

Put the file in the repository as .github/hooks/arcjet.json. The following excerpt shows the preToolUse entry from the Arcjet template. The full template also installs permissionRequest, userPromptSubmitted, userPromptTransformed, postToolUse, agentStop, sessionStart, and sessionEnd.

{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "http",
"url": "https://decide.arcjet.com/v1/agent-hooks/copilot?event=pre-tool-use",
"headers": {
"Authorization": "Bearer $ARCJET_KEY",
"X-Arcjet-Principal": "$USER"
},
"allowedEnvVars": ["ARCJET_KEY", "USER"],
"timeoutSec": 5
}
]
}
}

allowedEnvVars lets $ARCJET_KEY and $USER resolve in the headers. Export ARCJET_KEY where the agent runs, or replace the header with a literal value in a file that only an administrator can edit. Never commit a literal key to a repository.

X-Arcjet-Principal attributes a session to a developer. It's untrusted metadata. $USER is unset on Windows, so use $USERNAME there and add it to the allowedEnvVars list.

The hook URLs omit the surface parameter. The same file reaches the CLI and the cloud agent, so a hard-coded value would mislabel one of them. Arcjet records unknown when you omit it.

How do you lock Copilot hooks for an organization?

For the Copilot CLI, put the same JSON in /etc/github-copilot/policy.d/arcjet.json, owned by root and not group-writable or world-writable. Users can't disable a policy hook, so Arcjet stays in place on every session. Deploy the file with the mobile device management (MDM) or configuration management tooling you already use. A local administrator can still edit or remove it, as with any root-owned file.

For the cloud coding agent, the hooks come from .github/hooks/*.json on the default branch, so whoever can merge to that branch controls them. Add a firewall allow rule for decide.arcjet.com in the repository or organization's internet access settings, or the hook can't reach Arcjet.

GitHub routes Copilot traffic to a hostname that matches the user's plan, which lets you keep personal Copilot plans off managed networks. Allow *.business.githubcopilot.com or *.enterprise.githubcopilot.com, and block *.individual.githubcopilot.com. For the full set of controls, including the Enterprise Managed Users header, see how to block personal AI accounts on work laptops.

What can Copilot enforce, and what can't it?

Arcjet enforces tool-call policies on Copilot, which is where a coding agent acts. GitHub built Copilot to honor a denial on tool calls and ignore one on prompts. The following table maps each Execute on option in the Arcjet Console to the Copilot event it runs on.

Execute onCopilot eventResult
Tool callpreToolUse, permissionRequestEnforced. A denial stops the tool call.
PromptuserPromptSubmitted

Not enforced. Copilot drops hook output on this event. Arcjet records the decision and marks it as not enforced.

Model switchNoneNot available. Copilot has no model-switch hook.

Arcjet maps Copilot's tool names, such as powershell, view, rg, create, str_replace_editor, apply_patch, web_fetch, and MCP tools, onto one tool_kind value per category. A policy that compares tool_kind covers Copilot alongside Claude Code, Cursor, and OpenAI Codex. Start from the starter policies in the Arcjet Console, and run each in dry run to see what it would deny before it goes live. For the starter policies, see how to stop a coding agent running destructive commands, how to stop a coding agent reading .env files and credentials, and how to restrict which MCP servers a coding agent can use.

A Copilot denial carries both response shapes that Copilot surfaces read: a flat permissionDecision and the nested object. An allow is an empty object, {}. Arcjet never answers with an explicit allow, because Copilot treats one as a grant that skips its own permission flow.

What happens if the Copilot hook can't reach Arcjet?

GitHub built Copilot HTTP hooks to fail open: on a timeout, a network error, or a non-2xx response, the call proceeds without a policy decision. On Copilot, the availability and latency of the hook endpoint set how strong enforcement is. Arcjet evaluates at the edge in over 300 data centers, so it answers in a few tens of milliseconds, well inside the 5-second timeout. For the detail across vendors, and how to find sessions that ran without decisions, see Do coding agent hooks fail open?.

What do you pair with Arcjet on Copilot?

Arcjet decides every tool call that reaches the Copilot hooks. GitHub sets which surfaces and events the hooks reach, so pair Arcjet with the following controls:

  • VS Code agent mode: GitHub's agent hooks in VS Code are in preview and don't support the HTTP hook type. Use the Arcjet hooks on the CLI and the cloud agent, and network controls for which Copilot accounts can sign in at all.
  • Prompt denials: Copilot drops the output of a prompt hook. Arcjet records each prompt decision and marks it as not enforced, and tool-call policies stop what the agent tries to do next.
  • Model restrictions: Copilot hook payloads have no model field, so an allowed-models policy doesn't run on Copilot. Restrict models with a control outside the hooks, such as your organization's Copilot model policies in GitHub. For how the same policy behaves on other agents, see how to restrict which models coding agents can use.
  • Personal plans: allow the business and enterprise Copilot hostnames and block the individual one, as described in How do you lock Copilot hooks for an organization?.

How do you check the Copilot hooks work?

Run a harmless command through Copilot, then confirm it in Arcjet:

  1. Start a Copilot session in a repository that carries the hook file, and ask it to run a harmless command, such as listing a directory.
  2. In the Arcjet Console, open the site's Activity and confirm the session and the tool call appear.

Where does Arcjet fit for GitHub Copilot?

With Arcjet, every Copilot CLI and cloud agent tool call is decided against your policy before it runs. Arcjet gives you the following capabilities:

  • One policy across agents: the same Rego policies apply to Copilot, Claude Code, Cursor, and OpenAI Codex.
  • Enforcement developers can't remove: a root-owned policy file on developer machines and a hook file on the default branch, with policy changes in the Arcjet Console that take effect in real time without redeploying either.
  • Low added latency: Arcjet evaluates policies at the edge in over 300 data centers.
  • Visibility and export: every session and decision appears in the Arcjet Console, and decisions export to Datadog, Splunk, SentinelOne, Panther, and Amazon S3 (Enterprise plan).

To start, add the Arcjet Copilot template to one repository. Then publish the coding-agent.destructive-command starter policy in dry run, review the tool calls it would have denied in Activity, and set it live.

Frequently asked questions

Can hooks block GitHub Copilot tool calls?

Yes. GitHub Copilot honors a denial on preToolUse and permissionRequest in the Copilot CLI and the cloud coding agent. Arcjet answers those hooks and denies a shell command, file write, fetch, or MCP call before it runs, using the same policy it applies to Claude Code, Cursor, and OpenAI Codex.

Can a hook block a GitHub Copilot prompt?

No. GitHub Copilot drops hook output on userPromptSubmitted. Arcjet still records the decision and marks it as not enforced.

Do Arcjet hooks work in VS Code Copilot agent mode?

No. VS Code agent hooks are in preview and don't support the HTTP hook type, so the Arcjet hook file applies to the GitHub Copilot CLI and the cloud coding agent.

How do I stop developers disabling Copilot hooks?

For the GitHub Copilot CLI, put the hook JSON in /etc/github-copilot/policy.d/arcjet.json, root-owned and not group-writable or world-writable. Users can't disable a policy hook, though a local administrator can still edit the file. The cloud agent reads .github/hooks/*.json from the default branch, so control who can merge there.

Can I restrict which models developers use in Copilot with hooks?

No. GitHub Copilot hook payloads have no model field, so an allowed-models policy doesn't run on Copilot. Restricting Copilot models needs a control outside the hooks.

AI runtime security in your code

Protect your AI agent workflows with Arcjet

Add the Arcjet hook file to the Copilot CLI and cloud coding agent and enforce tool-call policies across your organization.