Phase 12: Security Surface Analysis
Analysis date: 2026-03-12
Scope: copilot-sdk repository — Node.js, Python, Go, and .NET SDK clients
Purpose: Documentation of existing security-relevant behaviors; no recommendations included.
1. Authentication Model
The SDK supports four authentication methods, documented in docs/auth/index.md. They are applied in a strict priority order.
1.1 Authentication Priority Chain
As stated in docs/auth/index.md:269-278:
| Priority | Method | Trigger |
|---|---|---|
| 1 | Explicit githubToken |
Token passed to SDK constructor |
| 2 | HMAC key | CAPI_HMAC_KEY or COPILOT_HMAC_KEY env vars |
| 3 | Direct API token | GITHUB_COPILOT_API_TOKEN + COPILOT_API_URL |
| 4 | Environment variable tokens | COPILOT_GITHUB_TOKEN then GH_TOKEN then GITHUB_TOKEN |
| 5 | Stored OAuth credentials | From previous copilot CLI login |
| 6 | GitHub CLI auth | gh auth credentials |
1.2 GitHub Signed-in User (Default)
The default path. Users authenticate via GitHub OAuth device flow. Credentials are stored in the system keychain by the CLI. The SDK reads these stored credentials automatically when no explicit token is provided (docs/auth/index.md:14-22).
All four SDKs create a client with no arguments to use this path:
- TypeScript
new CopilotClient()— nodejs/src/client.ts:229 - Python
CopilotClient()— python/copilot/client.py:126 - Go
copilot.NewClient(nil)— go/client.go:106 - .NET
new CopilotClient()— dotnet/src/Client.cs:103
1.3 OAuth GitHub App
Users authorize an OAuth app; the app receives a user access token (gho_ or ghu_ prefix) and passes it to the SDK via githubToken. Supported token prefixes are documented in docs/auth/index.md:192-198:
gho_— OAuth user access tokensghu_— GitHub App user access tokensgithub_pat_— Fine-grained personal access tokensghp_— Classic personal access tokens (NOT supported, deprecated)
1.4 Environment Variable Tokens
For CI/CD and server-to-server. The SDK auto-detects tokens from environment variables in this order (docs/auth/index.md:209-213):
COPILOT_GITHUB_TOKENGH_TOKENGITHUB_TOKEN
1.5 BYOK (Bring Your Own Key)
Bypasses GitHub Copilot authentication entirely. API keys from OpenAI, Azure AI Foundry, Anthropic, or compatible endpoints are passed directly in the session config. See Section 9 for full details.
1.6 Auth Validation When Using External Server
All four SDKs enforce that githubToken and useLoggedInUser cannot be combined with cliUrl. When cliUrl is set, the external CLI server manages its own auth:
- TypeScript nodejs/src/client.ts:215-219 — throws
Error - Python python/copilot/client.py:144-150 — raises
ValueError - Go go/client.go:144-146 — calls
panic() - .NET dotnet/src/Client.cs:133-136 — throws
ArgumentException
1.7 HMAC Authentication
The auth types system includes an "hmac" auth type, referenced in:
- nodejs/src/types.ts:977 —
authType?: "user" | "env" | "gh-cli" | "hmac" | "api-key" | "token" - dotnet/src/Types.cs:1689 — Enum documentation for
"hmac"variant
HMAC keys are read from CAPI_HMAC_KEY or COPILOT_HMAC_KEY environment variables (docs/auth/index.md:274). This is priority 2 in the auth chain, just below explicit githubToken.
1.8 Auth Status API
All SDKs expose a method to query authentication status via the auth.getStatus JSON-RPC call:
- TypeScript
getAuthStatus()— nodejs/src/client.ts:775-781 - Python
get_auth_status()— python/copilot/client.py:896-915 - Go
GetAuthStatus(ctx)— go/client.go:1053-1064 - .NET
GetAuthStatusAsync(ct)— dotnet/src/Client.cs:638-643
2. Token / Credential Handling
2.1 Token Flow: SDK to CLI
When a githubToken is provided to the SDK constructor, it is passed to the CLI subprocess via an environment variable, not via command-line arguments. The pattern is identical across all four SDKs:
- The SDK passes
--auth-token-env COPILOT_SDK_AUTH_TOKENas a CLI argument, telling the CLI to read the token from that env var. - The SDK sets
COPILOT_SDK_AUTH_TOKENin the subprocess environment to the token value.
This avoids the token appearing in ps or /proc/*/cmdline output.
Citations:
// nodejs/src/client.ts:1126-1139
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
// ...
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
# python/copilot/client.py:1300-1322 args.extend(["--auth-token-env", "COPILOT_SDK_AUTH_TOKEN"]) # ... env["COPILOT_SDK_AUTH_TOKEN"] = self.options["github_token"]
// go/client.go:1174-1210 args = append(args, "--auth-token-env", "COPILOT_SDK_AUTH_TOKEN") // ... c.process.Env = append(c.process.Env, "COPILOT_SDK_AUTH_TOKEN="+c.options.GitHubToken)
// dotnet/src/Client.cs:1023-1064 args.AddRange(["--auth-token-env", "COPILOT_SDK_AUTH_TOKEN"]); // ... startInfo.Environment["COPILOT_SDK_AUTH_TOKEN"] = options.GitHubToken;
2.2 Token Storage in SDK Memory
The githubToken is stored as a field on the client options object for the lifetime of the client:
- TypeScript nodejs/src/client.ts:151 —
githubToken?: stringin the privateoptionsfield - Python python/copilot/client.py:201-202 —
self.options["github_token"] = github_token - Go go/client.go:188-189 —
opts.GitHubToken = options.GitHubToken - .NET dotnet/src/Client.cs:1064 — set during
StartCliServerAsync
The token lives in process memory; none of the SDKs persist it to disk.
2.3 Auto-Login Suppression
When a githubToken is explicitly provided, all SDKs default useLoggedInUser to false, which causes --no-auto-login to be passed to the CLI:
- TypeScript nodejs/src/client.ts:249-250
- Python python/copilot/client.py:179-183
- Go go/client.go:1178-1186
- .NET dotnet/src/Client.cs:1029-1033
2.4 Environment Variable Inheritance
The CLI subprocess inherits the parent process's environment by default (filtered):
- TypeScript nodejs/src/client.ts:247 —
env: options.env ?? process.env, thenNODE_DEBUGis deleted (nodejs/src/client.ts:1134-1135) - Python python/copilot/client.py:1314-1318 —
env = dict(os.environ)if not overridden - Go go/client.go:199-201 —
opts.Env = os.Environ()if not set - .NET dotnet/src/Client.cs:1050-1059 — uses
ProcessStartInfo.Environment, removesNODE_DEBUG
All SDKs allow overriding the environment via env/Environment options, giving callers full control over what the CLI subprocess sees.
3. Permission System
The SDK implements a permission request/response system that mediates CLI requests for potentially sensitive operations (tool invocations, file access, etc.).
3.1 Mandatory Permission Handler
All four SDKs require an onPermissionRequest handler when creating or resuming a session. Attempting to create a session without one results in an error:
// nodejs/src/client.ts:540-543
if (!config?.onPermissionRequest) {
throw new Error("An onPermissionRequest handler is required ...");
}
# python/copilot/client.py:487-491
if not cfg.get("on_permission_request"):
raise ValueError("An on_permission_request handler is required ...")
// go/client.go:488-489
if config == nil || config.OnPermissionRequest == nil {
return nil, fmt.Errorf("an OnPermissionRequest handler is required ...")
}
// dotnet/src/Client.cs:389-393
if (config.OnPermissionRequest == null)
throw new ArgumentException("An OnPermissionRequest handler is required ...");
3.2 Permission Request Result Kinds
The permission system supports four result kinds, defined across all SDKs:
| Kind | String Value | Meaning |
|---|---|---|
| Approved | "approved" |
Permission granted |
| Denied by Rules | "denied-by-rules" |
Denied by policy/rules |
| Denied (No User) | "denied-no-approval-rule-and-could-not-request-from-user" |
Cannot ask user |
| Denied by User | "denied-interactively-by-user" |
User explicitly denied |
Reference: go/types.go:110-125, dotnet/src/Types.cs:270-336, nodejs/src/types.ts:243
3.3 Pre-Built Permission Handlers
Each SDK provides an approveAll / ApproveAll convenience handler that approves every request:
// nodejs/src/types.ts:251
export const approveAll: PermissionHandler = () => ({ kind: "approved" });
// go/permissions.go:4-11
var PermissionHandler = struct {
ApproveAll PermissionHandlerFunc
}{
ApproveAll: func(_ PermissionRequest, _ PermissionInvocation) (PermissionRequestResult, error) {
return PermissionRequestResult{Kind: PermissionRequestResultKindApproved}, nil
},
}
// dotnet/src/PermissionHandlers.cs:8-12
public static class PermissionHandler
{
public static PermissionRequestHandler ApproveAll { get; } =
(_, _) => Task.FromResult(new PermissionRequestResult { Kind = PermissionRequestResultKind.Approved });
}
3.4 Permission Protocol Versions
The SDK supports two protocol versions for permission handling:
- Protocol v2: The CLI sends
permission.requestRPC calls. The SDK registers a handler for these:- TypeScript nodejs/src/client.ts:1387-1391
- Python python/copilot/client.py:1434
- Go go/client.go:1365
- .NET dotnet/src/Client.cs:1196
- Protocol v3: The CLI sends permission requests as broadcast
permission.requestedevents, dispatched inCopilotSession._dispatchEvent(nodejs/src/client.ts:1375-1377).
Both v2 adapters are always registered because they are set up before version negotiation; a v3 server simply never invokes them.
3.5 Default Deny When No Handler
If a permission request arrives for a session that has no handler registered (or if the handler call fails), the default is deny with DeniedCouldNotRequestFromUser:
- Go go/client.go:1513-1517
- .NET dotnet/src/Session.cs:336-338
- TypeScript nodejs/src/session.ts:500-504
3.6 requestPermission Flag
All SDKs set requestPermission: true on session create/resume RPC calls, instructing the CLI to route permission decisions through the SDK rather than handling them internally:
- TypeScript nodejs/src/client.ts:588
- Python python/copilot/client.py:533
- Go go/client.go:529
4. Process Security
4.1 CLI Process Spawning
When not using an external server (cliUrl), each SDK spawns the Copilot CLI as a child process:
- TypeScript Uses
spawn()fromnode:child_process— nodejs/src/client.ts:1163-1176 - Python Uses
subprocess.Popen()— python/copilot/client.py:1331-1352 - Go Uses
os/exec.CommandContext()— go/client.go:1197 - .NET Uses
System.Diagnostics.Process— dotnet/src/Client.cs:1067-1068
4.2 Window Hiding on Windows
All four SDKs suppress console window creation on Windows to prevent visual disruption:
| SDK | Mechanism | File:Line |
|---|---|---|
| TypeScript | windowsHide: true in spawn options |
nodejs/src/client.ts:1167, 1174 |
| Python | subprocess.CREATE_NO_WINDOW creation flag |
python/copilot/client.py:1325 |
| Go | SysProcAttr.HideWindow = true |
go/process_windows.go:13-14 |
| .NET | ProcessStartInfo.CreateNoWindow = true |
dotnet/src/Client.cs:1047 |
On non-Windows for Go, configureProcAttr is a no-op (go/process_other.go:9-11).
4.3 Shell Execution Disabled
All SDKs explicitly avoid shell execution:
- TypeScript
spawn()is used directly (noshell: true) - Python Arguments are passed as a list to
Popen, not as a shell string - .NET
UseShellExecute = false— dotnet/src/Client.cs:1042
4.4 CLI Arguments
The SDK always passes a fixed set of flags to the CLI process:
--headless --no-auto-update --log-level <level>
Plus transport mode (--stdio or --port <n>), and optionally:
--auth-token-env COPILOT_SDK_AUTH_TOKEN(whengithubTokenis set)--no-auto-login(whenuseLoggedInUseris false)
These are hardcoded; user-provided cliArgs are prepended before SDK-managed args.
4.5 Process Lifecycle
Graceful stop: SDKs send stop/close signals and wait for process termination with a timeout:
- Python
terminate()thenwait(timeout=5)thenkill()— python/copilot/client.py:385-389 - TypeScript
kill()— nodejs/src/client.ts:421 - Go
killProcess()viaos.Process.Kill()— go/client.go:1282-1289 - .NET
Process.Kill()— dotnet/src/Client.cs:355
Force stop: All SDKs provide a forceStop() method that immediately kills the process without waiting:
- TypeScript
SIGKILL— nodejs/src/client.ts:497 - Go
osProcess.Kill()outside of start/stop mutex — go/client.go:401-404
4.6 Process Monitoring
Go implements explicit process monitoring with a goroutine (go/client.go:1297-1310) that signals a done channel on exit. The process pointer is stored atomically (go/client.go:95) to allow safe concurrent access for kill operations.
5. Input Validation
5.1 Constructor-Level Validation
All SDKs validate mutually exclusive options at construction time:
cliUrlis mutually exclusive withuseStdioandcliPath:- TypeScript nodejs/src/client.ts:205-207
- Go go/client.go:140-141
isChildProcessmust use stdio, notcliUrl:- TypeScript nodejs/src/client.ts:209-213
- Auth options cannot be used with external server: see Section 1.6
5.2 Protocol Version Verification
After connecting, all SDKs verify the CLI server's protocol version via a ping RPC call:
- TypeScript nodejs/src/client.ts:837-864 — checks
MIN_PROTOCOL_VERSION(2) togetSdkProtocolVersion() - Python python/copilot/client.py:54 —
MIN_PROTOCOL_VERSION = 2 - Go go/client.go:1140-1141 — rejects if server version is out of range
- .NET dotnet/src/Client.cs:974-998
If the version is outside the supported range, the connection is refused with a descriptive error.
5.3 CLI Path Verification
The Node.js SDK verifies the CLI binary exists before attempting to spawn:
// nodejs/src/client.ts:1148-1153
if (!existsSync(this.options.cliPath)) {
throw new Error(`Copilot CLI not found at ${this.options.cliPath}...`);
}
Python performs a similar check at python/copilot/client.py:1288-1289.
5.4 Port Parsing
For TCP mode, all SDKs parse the port from CLI stdout using regex:
- TypeScript
stdout.match(/listening on port (\d+)/i)— nodejs/src/client.ts:1189 - Python
re.search(r"listening on port (\d+)", ...)— python/copilot/client.py:1371 - Go
regexp.MustCompile("listening on port (\\d+)")— go/client.go:1254 - .NET
ListeningOnPortRegex()generated regex — dotnet/src/Client.cs:1102
5.5 Session Create RPC Payload
Tool definitions are serialized with name, description, and parameters before being sent to the CLI. Parameters go through JSON Schema conversion:
- TypeScript
toJsonSchema()at nodejs/src/client.ts:73-78 — handles both Zod schemas and raw JSON schemas - The
envValueModeis set to"direct"on all create/resume calls:- TypeScript nodejs/src/client.ts:594
- Python python/copilot/client.py:564
- Go go/client.go:508
5.6 Permission Request Payload Validation
v2 permission request handlers validate the incoming payload before processing:
- TypeScript nodejs/src/client.ts:1595-1596 — checks
sessionIdis string andpermissionRequestexists - Go go/client.go:1502-1503 — returns error code
-32602if session ID or request is empty - Python python/copilot/client.py:1650-1652 — raises
ValueErroron invalid payload
6. Secret Management
6.1 .gitignore Coverage
The repository uses per-directory .gitignore files. .env files are excluded across multiple SDK directories:
| .gitignore File | Excludes .env |
|---|---|
| nodejs/.gitignore:76-80 | .env, .env.development.local, .env.test.local, .env.production.local, .env.local |
| python/.gitignore:123 | .env |
| go/.gitignore:24 | .env |
| Root .gitignore:2 | docs/.validation/ (build artifacts only) |
| dotnet/.gitignore | Does not mention .env |
6.2 Security-Relevant Environment Variables
The following environment variables carry credentials:
| Variable | Purpose | Where Referenced |
|---|---|---|
COPILOT_SDK_AUTH_TOKEN |
Internal: token passed to CLI subprocess | All four SDK clients |
COPILOT_GITHUB_TOKEN |
GitHub token for Copilot (priority 1 env var) | docs/auth/index.md:210 |
GH_TOKEN |
GitHub CLI compatible token (priority 2) | docs/auth/index.md:211 |
GITHUB_TOKEN |
GitHub Actions compatible token (priority 3) | docs/auth/index.md:212 |
GITHUB_COPILOT_API_TOKEN |
Direct API token | docs/auth/index.md:275 |
COPILOT_API_URL |
API endpoint for direct API token mode | docs/auth/index.md:275 |
CAPI_HMAC_KEY / COPILOT_HMAC_KEY |
HMAC signing key | docs/auth/index.md:274 |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
OAuth app credentials (app code, not SDK) | docs/setup/github-oauth.md:100-101 |
FOUNDRY_API_KEY |
Azure AI Foundry API key (BYOK) | docs/auth/byok.md:41 |
OPENAI_API_KEY |
OpenAI API key (BYOK) | docs/auth/byok.md:212 |
ANTHROPIC_API_KEY |
Anthropic API key (BYOK) | docs/auth/byok.md:291 |
AZURE_OPENAI_KEY |
Azure OpenAI API key (BYOK) | docs/auth/byok.md:225 |
COPILOT_CLI_PATH |
CLI binary path override | go/client.go:205 |
6.3 NODE_DEBUG Suppression
All SDKs remove NODE_DEBUG from the CLI subprocess environment to prevent verbose debug output from polluting stdio transport:
- TypeScript nodejs/src/client.ts:1135
- .NET dotnet/src/Client.cs:1059
7. Network Security
7.1 Transport Modes
The SDK communicates with the CLI via two transport modes:
| Mode | Protocol | When Used |
|---|---|---|
| stdio (default) | JSON-RPC over stdin/stdout pipes | Local child process |
| TCP | JSON-RPC over raw TCP socket | External server (cliUrl) or port mode |
7.2 TCP Connection Details
TCP connections use plain TCP sockets — there is no TLS between SDK and CLI:
- TypeScript
new Socket().connect(port, host)— nodejs/src/client.ts:1342-1344 - Go
net.Dialer.DialContext(ctx, "tcp", address)with 10s timeout — go/client.go:1334-1337 - .NET
new TcpClient().ConnectAsync(host, port)— dotnet/src/Client.cs:1173-1174
The default host is localhost. The docs note that when using cliUrl in production, the network path between SDK and CLI should be secured (docs/setup/backend-services.md:471): "No built-in auth between SDK and CLI — Secure the network path (same host, VPC, etc.)"
7.3 CLI-to-Cloud Communication
The SDK itself does not make direct API calls to model providers. The CLI subprocess handles all outbound network communication to:
- GitHub Copilot API
- Azure AI Foundry / Azure OpenAI endpoints
- OpenAI API
- Anthropic API
- Local model servers (Ollama, Foundry Local)
BYOK provider baseUrl values are passed through the session config to the CLI, which makes the actual HTTP(S) requests.
7.4 URL Parsing
The cliUrl parameter is parsed to extract host and port. The Node.js SDK has a parseCliUrl method (nodejs/src/client.ts:224). The Go SDK has a parseCliUrl function (go/client.go:151). The Python SDK's test suite confirms HTTPS URLs are accepted (python/test_client.py:58-59).
7.5 Proxy Support
There is no explicit proxy configuration in the SDK layer. Proxy handling, if any, would be in the CLI subprocess, which inherits the parent process's environment variables (including standard proxy env vars like HTTP_PROXY, HTTPS_PROXY).
8. Attack Surface
8.1 Entry Points
| Entry Point | Description | Risk Context |
|---|---|---|
| SDK Constructor | Accepts cliPath, cliUrl, githubToken, env vars |
cliPath is used directly as executable path |
| Session Create | Accepts model name, provider config (with API keys), tools, system message, working directory | Tool definitions and system messages are passed to CLI |
| Message Send | User prompts sent to CLI | Prompt content goes to model provider |
| Tool Handler Callbacks | SDK-registered tools invoked by CLI | Tool arguments come from model output |
| Permission Handler | SDK-registered handler invoked by CLI | Must decide whether to allow actions |
| TCP Listener | When CLI runs in headless mode on a port | Network-accessible JSON-RPC endpoint |
8.2 Tool Registration and Execution
Custom tools are registered at session creation time. The tool handler receives arguments from the CLI, which come from model output:
// nodejs/src/types.ts:132-142
export interface ToolInvocation {
sessionId: string;
toolCallId: string;
toolName: string;
arguments: unknown; // From model output
}
The arguments field is typed as unknown — the SDK does not validate tool arguments against the declared schema. Schema validation (if any) occurs in the CLI or model provider side.
8.3 Tool Override Mechanism
Tools can be marked with overridesBuiltInTool: true (nodejs/src/types.ts:169), which allows SDK-registered tools to replace CLI built-in tools. Without this flag, a name clash results in an error.
8.4 System Message Injection Surface
Session creation accepts a systemMessage parameter (nodejs/src/client.ts:584) that is sent to the CLI. The SystemMessageAppendConfig type (nodejs/src/types.ts:202-208) supports an "append" mode with a content string. This content is concatenated into the system prompt.
8.5 Working Directory
Session creation accepts a workingDirectory parameter (nodejs/src/client.ts:591) that controls where the CLI operates. This path is not validated by the SDK before being sent to the CLI.
8.6 MCP Server Configuration
Session creation accepts an mcpServers parameter (nodejs/src/client.ts:593) for Model Context Protocol server definitions, which the CLI connects to as additional tool sources.
8.7 Custom Agent Configuration
Session creation accepts customAgents and agent parameters (nodejs/src/client.ts:595-596) that configure agent behavior. The agent config can include MCP server references (python/copilot/client.py:1270).
8.8 Headless TCP Mode Exposure
When the CLI runs in headless mode (--headless --port <n>), it exposes a JSON-RPC server on a TCP port with no built-in authentication between SDK and CLI. The docs explicitly note this: docs/setup/backend-services.md:471.
8.9 cliArgs Passthrough
All SDKs accept user-provided cliArgs that are prepended to SDK-managed arguments:
- TypeScript nodejs/src/client.ts:1111 —
...this.options.cliArgs - Python python/copilot/client.py:1292-1293
- Go go/client.go:1164
- .NET dotnet/src/Client.cs:1007-1009
These args are not sanitized by the SDK before being passed to the process.
9. BYOK (Bring Your Own Key)
9.1 Overview
BYOK mode allows using the SDK without a GitHub Copilot subscription by providing API keys from third-party model providers. Documented in docs/auth/byok.md.
9.2 Supported Providers
| Provider | type Value |
Authentication |
|---|---|---|
| OpenAI | "openai" |
apiKey or bearerToken |
| Azure OpenAI / Azure AI Foundry | "azure" |
apiKey |
| Anthropic | "anthropic" |
apiKey |
| Ollama (local) | "openai" |
None required |
| Microsoft Foundry Local | "openai" |
None required |
Reference: docs/auth/byok.md:7-14
9.3 Provider Config Fields
From docs/auth/byok.md:172-181:
| Field | Type | Description |
|---|---|---|
type |
"openai" / "azure" / "anthropic" |
Provider type |
baseUrl |
string | Required. API endpoint URL |
apiKey |
string | API key (optional for local providers) |
bearerToken |
string | Bearer token auth (takes precedence over apiKey) |
wireApi |
"completions" / "responses" |
API format |
azure.apiVersion |
string | Azure API version |
9.4 Bearer Token Limitations
Bearer tokens are static only (docs/auth/byok.md:307): "The bearerToken option accepts a static token string only. The SDK does not refresh this token automatically."
9.5 Identity Provider Limitations
BYOK does not support (docs/auth/byok.md:426-430):
- Microsoft Entra ID (Azure AD) managed identities or service principals
- Third-party OIDC, SAML, or federated identity
- Azure Managed Identity natively
9.6 Azure Managed Identity Workaround
docs/setup/azure-managed-identity.md documents a pattern where DefaultAzureCredential from the Azure Identity library obtains a short-lived bearer token, which is then passed as bearer_token in the BYOK provider config. The application is responsible for token refresh before expiry (~1 hour).
9.7 API Key Transmission to CLI
BYOK API keys and bearer tokens are passed in the session create RPC payload as part of the provider configuration. In Python, bearer_token is mapped to bearerToken for wire format:
# python/copilot/client.py:1239-1240
if "bearer_token" in provider:
wire_provider["bearerToken"] = provider["bearer_token"]
The keys travel over the JSON-RPC connection (stdio pipes or TCP socket) to the CLI, which then uses them for outbound API calls.
9.8 Custom Model Listing
BYOK supports onListModels override (docs/auth/byok.md:311-417), which completely replaces the CLI's models.list RPC. Results are cached after first call.
10. Security Policies
10.1 SECURITY.md
The repository's SECURITY.md (root level) establishes:
- Disclosure policy: Coordinated disclosure via email to
opensource-security@github.com(SECURITY.md:15) - No public disclosure: Vulnerabilities must not be reported via GitHub issues, discussions, or pull requests (SECURITY.md:13)
- Bug bounty scope: Open source repositories are outside GitHub's bug bounty scope (SECURITY.md:7)
- Safe harbor: References GitHub's Safe Harbor Policy (SECURITY.md:31)
10.2 Required Disclosure Information
From SECURITY.md:17-25, reporters are asked to provide:
- Type of issue (buffer overflow, SQL injection, XSS, etc.)
- Full paths of affected source files
- Location of affected code (tag/branch/commit or URL)
- Special configuration to reproduce
- Step-by-step reproduction instructions
- Proof-of-concept or exploit code
- Impact assessment
10.3 Copyright and Ownership
The source code carries Microsoft copyright headers:
- TypeScript nodejs/src/client.ts:1-3 — "Copyright (c) Microsoft Corporation. All rights reserved."
- .NET dotnet/src/Client.cs:1-3 — same
10.4 Protocol Version as Security Boundary
The protocol version negotiation (MIN_PROTOCOL_VERSION = 2) acts as a compatibility gate. A mismatched server is rejected before any session operations occur. This prevents the SDK from communicating with potentially incompatible or outdated CLI servers that may have different security behaviors.
Summary of Security-Relevant Architecture
flowchart LR
A["<b>Application</b><br/>Provides token<br/>Registers tools<br/>Handles permissions<br/>Sends prompts"] --> B["<b>SDK Client</b><br/>(in-process)<br/>Validates opts<br/>Spawns CLI<br/>JSON-RPC conn<br/>Routes events<br/>Env var token"]
B --> C["<b>Copilot CLI</b><br/>(subprocess / TCP)<br/>Auth to GitHub<br/>BYOK to APIs<br/>Tool execution<br/>Model calls"]
C --> D["<b>Cloud Services</b><br/>GitHub API<br/>OpenAI<br/>Azure AI<br/>Anthropic"]
Key security boundaries:
- Application <-> SDK: In-process trust boundary; SDK validates constructor options and requires permission handlers.
- SDK <-> CLI: Process boundary; tokens passed via env vars, communication over JSON-RPC (stdio or TCP). No TLS on TCP path.
- CLI <-> Cloud: Network boundary; the CLI handles all outbound HTTPS to model providers and GitHub APIs.