github / github/copilot-cli

sdk: "./sdk" export throws at import time — native module resolved outside its own security boundary

Open
#4,785 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

triage
Dominant language
Shell
Stars
11.2k
Forks
1.9k
Avg merge
14h 16m
Merged PRs (30d)
6

Description

Package: @github/copilot
Version affected: 1.0.11
Platform: darwin-arm64

Summary

package.json's "exports" map points the documented import path @github/copilot/sdk at sdk/index.js. That bundle's own require() wrapper enforces a same-directory security check — it resolves the requested module's real path, computes it relative to the bundle's own directory (sdk/), and throws if the relative path starts with .. (i.e. the target lives outside sdk/).

The native pty.node addon this bundle needs at import time lives in the package root's prebuilds/<platform>/ directory — one level outside sdk/. So the check the bundle enforces on itself is violated by the bundle's own required dependency, and import "@github/copilot/sdk" (or any import of sdk/index.js, including by absolute path) throws before a CopilotClient can even be constructed.

Reproduction

// Fails on darwin-arm64, @github/copilot@1.0.11:
import("/opt/homebrew/lib/node_modules/@github/copilot/sdk/index.js")
  .then(m => console.log("OK", Object.keys(m)))
  .catch(e => console.error("ERR", e.message));

The surfaced error is misleading: it reads as Cannot find module './prebuilds/<platform>/pty.node', which looks like an ordinary missing-file error. It is not — it's the last of six internal resolution attempts inside a try/catch loop, and it overwrites the real, earlier error: Requiring module outside of application is a security concern, thrown by the custom require() wrapper on the 5th attempt. Confirmed by instrumenting Module._resolveFilename directly.

Impact

Anyone following the package's own documented entry point (@github/copilot/sdk) for the CLI's JSON-RPC SDK hits this immediately — the import throws, not a runtime session error.

Workaround found

Importing the older copilot-sdk/index.js bundle directly by absolute path instead of the documented @github/copilot/sdk specifier works — it exposes the identical public API (CopilotClient, CopilotSession, defineTool, approveAll, SYSTEM_PROMPT_SECTIONS), uses a plain createRequire(import.meta.url) with no directory-boundary check, and is unaffected.

Suggested fix directions

  • Either place sdk/'s security check's allowed root one level higher (package root, not sdk/), or ship prebuilds/ (or a copy/symlink of the needed native module) inside sdk/ so the resolved path never crosses the boundary the check enforces.
  • Fix the swallowed-error behavior regardless: the security-check error should not be silently overwritten by a later, unrelated "module not found" from a subsequent resolution attempt in the same try/catch loop — it hides the real cause from anyone hitting this for the first time.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with package.json's exports map and sdk/index.js, then reproduce the darwin-arm64 import using the command shown. Trace the resolution attempts to prebuilds//pty.node and the boundary check, including the swallowed error. Done means @github/copilot/sdk imports successfully and failures preserve the original security-check cause.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
cli, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.