Skip to content

macOS sandbox:  allowLocalNetwork: true  still generates  hostLoopback: deny  in proxy mode, blocking Gradle on CLI 1.0.107 #5390

Open

Description

@rickard-von-essen

Describe the bug

Summary

Gradle fails inside the Copilot CLI macOS sandbox because its client cannot connect to its localhost daemon, despite the configuration explicitly enabling outbound and local networking.

The daemon starts successfully and listens on  127.0.0.1. However, the generated policy for the failing command contains:

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

The Gradle daemon in that run listened on port  57903 , not the permitted proxy port  57901 .

An independent socket probe also confirmed that binding and listening on localhost succeeds, while connecting to that listener fails with  EPERM .

This appears to be a persistence or regression of #5105, which is currently closed.

Environment

  • GitHub Copilot CLI: 1.0.107
  • macOS: 26.7.1
  • Architecture: arm64
  • Gradle wrapper: 9.8.2
  • Java: Eclipse Temurin 25.0.1

The project is a small standalone Java application using Gradle’s application plugin.

Relevant configuration

The following is a sanitized excerpt. An additional private artifact-registry hostname and unrelated settings have been omitted.

{
  "sandbox": {
    "enabled": true,
    "allowBypass": true,
    "allowDevToolAccess": true,
    "addCurrentWorkingDirectory": true,
    "auth": {
      "git": true,
      "gh": true
    },
    "userPolicy": {
      "network": {
        "allowOutbound": true,
        "allowLocalNetwork": true,
        "allowedHosts": [
          "127.0.0.1",
          "services.gradle.org"
        ],
        "blockedHosts": []
      }
    }
  }
}

Additional filesystem grants provide read/write access to the Gradle user home and read access to the JDK/toolchain locations. The user-level filesystem deny list is empty.

Steps to reproduce

  1. On macOS, use Copilot CLI  1.0.107  with sandboxing enabled.
  2. Enable outbound and local networking, with a non-empty host allowlist that includes  127.0.0.1 . Git and GitHub CLI credential masking are also enabled in this reproduction.
  3. Open a Java project using the Gradle  9.8.2  wrapper and Temurin Java  25.0.1 .
  4. Ask Copilot to execute:
    ./gradlew build --debug --no-daemon
  5. Inspect the debug output and the generated command policy under:
    ~/.copilot/session-state//policies/

Expected behavior

With  allowLocalNetwork: true , sandboxed commands should be able to make direct TCP connections to localhost, including to build daemons they start themselves, subject to any explicitly enforced managed restrictions.

A deny-by-default egress policy is understandable when a proxy enforces the remote host allowlist. However, the requested loopback permission should be represented by an appropriate loopback exception.

If local networking intentionally means only proxy-mediated HTTP access, the documentation and settings UI should clearly distinguish that from direct localhost TCP access.

Actual behavior and diagnostic evidence

The daemon starts successfully

Gradle reports:

Successfully started process 'Gradle build daemon'

The daemon log confirms that it is listening on:

127.0.0.1:57903

The client then attempts that connection and immediately reports:

Trying to connect to address /127.0.0.1.
Cannot connect to address /127.0.0.1, skipping.

The command exits with code 1:

Could not connect to the Gradle daemon.

No build tasks execute.

The effective policy denies host loopback

The policy corresponding to the debug build contains:

"ingress": {
  "default": "allow",
  "hostLoopback": "deny"
}

Its proxy endpoint is http://127.0.0.1:57901.

This is consistent with allowing listeners and access to the proxy, while denying direct connections to other localhost ports. Adding  127.0.0.1 to allowedHosts does not remove this effective denial.

A standalone socket probe reproduces the denial

A Python probe created a TCP listener on an ephemeral IPv4 loopback port and attempted to connect to it from the same process:

Loopback bind/listen succeeded: 127.0.0.1:57947
Loopback connect failed: PermissionError, errno=1, Operation not permitted

This isolates the failure from Gradle, dependency resolution, and external networking.

Proxy variables are injected

A subsequent sandboxed command received all uppercase and lowercase forms of HTTP_PROXY, HTTPS_PROXY, and ALL_PROXY, pointing to a local proxy. NO_PROXY and no_proxy were unset.

Gradle’s daemon connection uses direct TCP, not HTTP, so these proxy variables cannot carry the daemon protocol.

--no-daemon does not avoid the connection

Gradle still launches a single-use daemon because the wrapper JVM does not satisfy the build’s JVM requirements. The debug output identifies insufficient maximum heap size; the wrapper starts with -Xmx64m.

These commands all failed with the daemon connection error:

./gradlew build
./gradlew build --no-daemon -Dorg.gradle.jvmargs=
./gradlew build --debug --no-daemon

Requested investigation

Please check how sandbox.userPolicy.network.allowLocalNetwork maps to MXC’s network.ingress.hostLoopback when proxy enforcement is active.

In this reproduction, local networking is requested, but the generated policy still sets hostLoopback: "deny". Please clarify whether this is a policy-generation defect, a managed-policy override, or an intentional restriction.

If intentional, please document a supported way to permit build-daemon loopback connections without disabling the sandbox or removing remote host filtering.

Affected version

No response

Steps to reproduce the behavior

No response

Expected behavior

No response

Additional context

No response

Activity

  1. self-assigned this
    on Nov 9, 2026
  2. added
    area:permissionsTool approval, security boundaries, sandbox mode, and directory restrictions
    and removed on Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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