Skip to content

macOS sandbox blocks Gradle daemon connection despite local networking being allowed #5105

Closed

Description

@nugarov

Describe the bug

Gradle cannot execute even an offline help task inside the Copilot CLI macOS sandbox when outbound and local networking are configured as allowed.

The Gradle daemon starts successfully and listens on localhost, but the Gradle client cannot connect to it:

java.net.SocketException: Operation not permitted

The identical command succeeds with the Copilot sandbox disabled. Restarting Copilot with the sandbox enabled does not resolve the failure.

Affected version

Copilot CLI 1.0.95; also observed on 1.0.93.

Steps to reproduce the behavior

  1. On macOS, enable the Copilot CLI sandbox and allow both outbound and local networking. The configured network settings are:

    {
      "allowOutbound": true,
      "allowLocalNetwork": true,
      "allowedHosts": [],
      "blockedHosts": []
    }

    /sandbox policy reports outbound and local networking as allowed.

  2. In a Gradle 9.1.0 wrapper project with Java 21 selected, ask Copilot to execute:

    ./gradlew --offline --no-daemon --console=plain --stacktrace help
  3. Gradle launches a single-use daemon to honor the project's JVM settings. The daemon binds a localhost port, but the client connection fails with Operation not permitted, before any task executes.

  4. Disable the Copilot sandbox and repeat the identical command. It succeeds, executing only help.

Expected behavior

When local networking is explicitly allowed, Gradle should be able to connect to its localhost daemon.

If that setting intentionally excludes host-loopback connections, the policy display should make this distinction clear and document a supported way to permit the required connection without disabling the entire sandbox.

Additional context

Environment: macOS 26.6.2, ARM64; Gradle wrapper 9.1.0; Java Amazon Corretto 21.0.6.7.1.

The command uses offline mode, so dependency downloads are not involved. --no-daemon still launches a single-use daemon in this project.

Kernel-confirmed denial

In one sandbox-enabled run, the daemon listened on 127.0.0.1:53774. The macOS kernel log recorded:

Sandbox: java(5913) deny(1) network-outbound remote:*:53774

Effective policy from a separate failing run

Copilot's debug log identified the generated policy used for the command. Its network section was:

{
  "network": {
    "egress": {
      "default": "deny"
    },
    "ingress": {
      "default": "allow",
      "hostLoopback": "deny"
    },
    "runtimeConfig": {
      "networkProxy": "http://127.0.0.1:55437"
    }
  }
}

In that run, the Gradle daemon listened on 127.0.0.1:55438, not the proxy port 55437.

This appears consistent with the client being denied a direct host-loopback connection despite allowLocalNetwork: true. Gradle's daemon protocol uses raw TCP, so the HTTP proxy cannot carry that connection.

Please investigate whether allowLocalNetwork is being mapped correctly to the effective macOS policy, or whether a separate supported host-loopback permission is required.

Related reports

  • github/copilot-cli#4854: a Windows /sandbox policy display issue. Unlike that report, the display here says networking is allowed, while the generated policy and kernel denial demonstrate an actual blocked connection.
  • github/copilot-cli#4516: JVM filesystem-grant failures on macOS, with a recent comment also describing remote-repository/proxy problems. This report concerns the local Gradle daemon connection, without dependency downloads.

Only sanitized excerpts are included here; no project source, credentials, or full conversation/debug logs are attached.

Activity

  1. added
    area:permissionsTool approval, security boundaries, sandbox mode, and directory restrictions
    on Oct 10, 2026
  2. bart-westenenk-bex commented on Oct 12, 2026

    @bart-westenenk-bex

    We are also running into this with a database container in docker running locally and connecting to it through a rails application. This is a major blocker for using sandboxes properly as agents are not able to run our test suite.

  3. lbilger commented on Oct 12, 2026

    @lbilger

    I see a similar problem connecting to the Kotlin daemon in a Maven/Kotlin build.

  4. jwisniewski commented on Oct 14, 2026

    @jwisniewski

    I have exact issue with GitHub Copilot app (v1.1.29, macOS), with sandbox configured within its UI. I believe this desktop app uses embedded copilot cli instance with version: 1.0.95-3.

  5. bart-westenenk-bex commented on Oct 15, 2026

    @bart-westenenk-bex

    This is probably an issue that will be occurring for everybody.
    Anthropic has the following in the docs for their own sandboxing runtime: https://github.com/anthropics/sandbox-runtime#dual-isolation-model

    macOS: The Seatbelt profile allows communication only to a specific localhost port. The proxies listen on this port, creating a controlled channel for all network access

    Since CLI doesn't allow specifying a port, there is no way you can setup the seatbelt profile correctly. Hence it will never work on MacOS.

  6. geuben commented on Oct 15, 2026

    @geuben

    Same problem with the VS Code agent, which uses the same runtime (@github/copilot-sdk 1.0.17, VS Code 1.141.0, macOS 27.0.1). The per-command policy the runtime writes to ~/.copilot/session-state/<session>/policies/*.json explains it. Measured from inside the sandbox:

    allowLocalNetwork: false (default) allowLocalNetwork: true
    Policy network ingress.default: deny, hostLoopback: deny ingress.default: allow, hostLoopback: deny
    listen on 127.0.0.1 Denied Works
    Direct connect() to loopback, including to a server the sandboxed process started EPERM EPERM
    Through the runtime's HTTP_PROXY (plain request and CONNECT) 403 200

    allowLocalNetwork opens ingress but never sets hostLoopback: allow, so the generated Seatbelt profile keeps (deny network-outbound (remote ip "localhost:*")). The only loopback path is the built-in proxy, which only proxy-aware HTTP clients use. Anything that connects directly fails with EPERM: the Gradle and Kotlin daemons here, Postgres and Prisma against a local Docker database, Temporal's test servers, and supertest.

    Two asks:

    1. Per-port host loopback. For example network.ingress.hostLoopbackPorts: [5432, 6379], or port-level entries such as localhost:5432 in allowedHosts that apply to both direct connections and the proxy. The Seatbelt profile builder already emits a single-port rule for the proxy, (allow network-outbound (remote ip "localhost:<proxy port>")), so emitting the same rule for listed ports looks like a small change.
    2. Loopback to servers the sandboxed command started itself (build daemons, test servers on ephemeral ports), without opening the host's other loopback services. I don't know whether Seatbelt can express this directly; even a documented "allow all loopback" opt-in would unblock people, as long as it isn't the only option.

    Why "allow all loopback" shouldn't be the only option: on a dev machine, loopback also carries Node inspector (9229) and Chrome remote debugging (9222) ports, kubectl port-forwards, and other projects' databases. Reaching some of these is a sandbox escape in itself. With allowLocalNetwork: true, the built-in proxy already relays to loopback with no port filter. We verified that only against a server started inside the sandbox, but per-port rules would tighten that path too.

    microsoft/mxc#1506

  7. caarlos0 commented on Oct 20, 2026

    @caarlos0
    Member

    this should be fixed in the next version, thanks for reporting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:permissionsTool approval, security boundaries, sandbox mode, and directory restrictions

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions