Skip to content

[test] Add tests for proxy.(*proxyHandler).handleWithDIFC - #13659

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
copilot/add-handlewithdifc-coverage-tests-502199c013b59588
Open

github-actions[bot] wants to merge 1 commit into
mainfrom
copilot/add-handlewithdifc-coverage-tests-502199c013b59588

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Test Coverage Improvement: handleWithDIFC

Function Analyzed

  • Package: internal/proxy
  • Function: (*proxyHandler).handleWithDIFC
  • Previous Coverage: 91.7%
  • New Coverage: 100.0%
  • Complexity: High (gocyclo cyclomatic complexity 36 — the highest-complexity function under 95% coverage in the codebase, after excluding heavy integration entrypoints already covered by prior test-coverage-improver runs: cmd.run, cmd.runProxy, config.LoadFromFile, middleware.wrapToolHandler, server.callBackendTool, config.validateGatewayConfigWithAgentRequirement, guard.buildStrictLabelAgentPayload)

Why This Function?

handleWithDIFC runs the 6-phase DIFC (Decentralized Information Flow Control) enforcement pipeline for every proxied GitHub API request — it's one of the most security-critical, branch-heavy functions in the gateway. Several error/enclave-mode branches were never exercised by existing tests, including how Phase 4/5 failures are handled differently in enclave mode vs. normal mode, and the final JSON serialization failure path.

Tests Added

New file internal/proxy/handler_difc_coverage_test.go (6 tests):

  • TestServeHTTP_GraphQLBodyReadErrorio.ReadAll failure while reading a GraphQL POST body in ServeHTTP
  • TestServeHTTP_GraphQLIntrospectionUpstreamFailure — introspection query forwarding to an unreachable upstream (resp == nil early return)
  • TestHandleWithDIFC_LabelResponseError_EnclaveMode — Phase 4 LabelResponse error in enclave mode → writeEnclaveDenied instead of the coarse-result fallback
  • TestHandleWithDIFC_Phase5Error_NonEnclave — Phase 5 FilterAndConvertLabeledData error, non-enclave mode → falls back to an empty response
  • TestHandleWithDIFC_Phase5Error_EnclaveMode — same Phase 5 error, enclave mode → writeEnclaveDenied
  • TestHandleWithDIFC_FinalMarshalError — final json.Marshal(finalData) failure when serializing the filtered response (unmarshalable chan int payload)

All new tests use the existing stubGuard/newTestServerWithStub/mockUpstream test helpers already present in the package, plus a small new errorToResultLabeledData test double (a difc.LabeledData whose ToResult() always errors) to deterministically drive the Phase 5 error path.

Coverage Report

Function: internal/proxy.(*proxyHandler).handleWithDIFC
Before: 91.7% coverage
After:  100.0% coverage
Improvement: +8.3%

Package internal/proxy:
Before: 95.6%
After:  96.7%

Test Execution

=== RUN   TestServeHTTP_GraphQLBodyReadError
--- PASS: TestServeHTTP_GraphQLBodyReadError (0.00s)
=== RUN   TestServeHTTP_GraphQLIntrospectionUpstreamFailure
--- PASS: TestServeHTTP_GraphQLIntrospectionUpstreamFailure (0.00s)
=== RUN   TestHandleWithDIFC_LabelResponseError_EnclaveMode
--- PASS: TestHandleWithDIFC_LabelResponseError_EnclaveMode (0.00s)
=== RUN   TestHandleWithDIFC_Phase5Error_NonEnclave
--- PASS: TestHandleWithDIFC_Phase5Error_NonEnclave (0.00s)
=== RUN   TestHandleWithDIFC_Phase5Error_EnclaveMode
--- PASS: TestHandleWithDIFC_Phase5Error_EnclaveMode (0.00s)
=== RUN   TestHandleWithDIFC_FinalMarshalError
--- PASS: TestHandleWithDIFC_FinalMarshalError (0.00s)
PASS
ok  	github.com/github/gh-aw-mcpg/internal/proxy	0.021s

make agent-finished results: Go format ✅, build ✅, unit tests ✅ (all packages), integration tests ✅. golangci-lint was skipped (not installed in this sandbox — consistent with prior coverage-improver runs). The Rust guard unit test step failed only due to a sandbox network block on crates.io (pre-existing environment limitation unrelated to this change, consistent with all prior runs of this workflow).


Generated by Test Coverage Improver
Next run will target the next most complex under-tested function (candidates recorded in cache-memory progress notes)

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • api.github.com
  • example.com
  • index.crates.io
  • nonexistent.local
  • slow.example.com
  • thishostdoesnotexist12345.com

[!TIP]
api.github.com is blocked because GitHub API access uses the built-in GitHub tools by default. Instead of adding api.github.com to network.allowed, use tools.github.mode: gh-proxy for direct pre-authenticated GitHub CLI access without requiring network access to api.github.com:

tools:
  github:
    mode: gh-proxy

See GitHub Tools for more information on gh-proxy mode.

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.github.com"
    - "example.com"
    - "index.crates.io"
    - "nonexistent.local"
    - "slow.example.com"
    - "thishostdoesnotexist12345.com"

See Network Configuration for more information.

Generated by Test Coverage Improver · copilot · auto · 222.3 AIC · ⊞ 10.5K ·

- Add TestServeHTTP_GraphQLBodyReadError covering the io.ReadAll error
  path when reading a GraphQL POST body in ServeHTTP.
- Add TestServeHTTP_GraphQLIntrospectionUpstreamFailure covering the
  resp==nil early return when the introspection forward fails.
- Add TestHandleWithDIFC_LabelResponseError_EnclaveMode and
  TestHandleWithDIFC_Phase5Error_EnclaveMode covering the enclave-mode
  branches of the Phase 4/5 error handlers (writeEnclaveDenied instead
  of the coarse fallback).
- Add TestHandleWithDIFC_Phase5Error_NonEnclave covering the non-enclave
  Phase 5 FilterAndConvertLabeledData error fallback to an empty
  response.
- Add TestHandleWithDIFC_FinalMarshalError covering the final
  json.Marshal failure branch when serializing the filtered response.

Coverage for proxy.(*proxyHandler).handleWithDIFC rose from 91.7% to
100.0% (gocyclo complexity 36, the highest-complexity under-95%
function in the codebase after excluding already fully-tested heavy
entrypoints). internal/proxy package coverage rose from 95.6% to 96.7%.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lpcox
lpcox marked this pull request as ready for review September 22, 2026 16:51
Copilot AI balanced review requested due to automatic review settings September 22, 2026 16:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The network-dependent test must use a deterministic mocked transport before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds targeted tests for previously uncovered DIFC and GraphQL error paths.

Changes:

  • Adds six error-path tests.
  • Covers enclave behavior, upstream failures, and serialization errors.
  • Adds deterministic DIFC test doubles.
File Summary Review
internal/​proxy/​handler_difc_coverage_test.go Adds focused proxy/DIFC coverage tests. Moderate (3 votes): Replace the real TCP connection to port 1 with a mocked transport to deterministically exercise the resp == nil branch.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +45 to +46
s := newTestServer(t, "http://127.0.0.1:1") // connection refused
h := &proxyHandler{server: s}
@github-actions

Copy link
Copy Markdown
Contributor Author

🔒 mcpg Read-Only Stress — default

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: default (normal container isolation)

Part Surface Op Result Expected Status
A MCP reads data ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) all 6 targets absent from tool catalog (unknown tool, -32602) BLOCKED ⚠️
C CLI reads data ALLOWED
D CLI REST writes gh unauthenticated, not attempted BLOCKED ⚠️
E CLI GraphQL mutations gh unauthenticated, not attempted BLOCKED ⚠️

Overall: INCONCLUSIVE

No write leaked. Gaps: (1) Part B — the github CLI's tool catalog exposes only 23 read-only tools (list/get/search); write tool names (add_issue_comment, star_repository, issue_write, create_branch, create_or_update_file, create_pull_request) are entirely absent, so refusals only confirm backend/toolset config (gh-aw's forced GITHUB_READ_ONLY=1), not mcpg's own gateway-level DIFC/guard blocking layer. (2) Parts D/E — gh reported "not logged into any GitHub hosts" / no GH_TOKEN, so the REST/GraphQL write-blocking boundary could not be exercised this run.

🔒 mcpg read-only stress (default AWF runtime) by Read-Only Stress: default runtime

@github-actions

Copy link
Copy Markdown
Contributor Author

🔒 mcpg Read-Only Stress — gvisor

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: gvisor

Part Surface Op Result Expected Status
A MCP reads (list_issues, list_pull_requests, get_file_contents, list_commits) data returned ALLOWED
B MCP writes (star_repository, add_issue_comment, issue_write, create_branch, create_or_update_file, create_pull_request) all 6 targets "unknown tool" — absent from the 23-tool read-only catalog exposed to this workflow; no gateway-level refusal could be exercised BLOCKED ⚠️
C CLI reads (github CLI proxy: list_issues, list_pull_requests, get_file_contents, list_commits) data returned ALLOWED
D CLI REST writes (reaction/star/issue create) gh reported "not logged into any GitHub hosts" / no GH_TOKEN — unauthenticated, no write attempted BLOCKED ⚠️
E CLI GraphQL mutations same gh auth gap as Part D — no mutation attempted BLOCKED ⚠️

Overall: INCONCLUSIVE

No write leaked through any surface in this run. The INCONCLUSIVE marks reflect known methodology gaps, not enforcement failures:

  • Part B: gh-aw's tools.github: wrapper launches the backend with GITHUB_READ_ONLY=1, so write tools never appear in the exposed catalog — this is the gh-aw framework's own defense-in-depth, not mcpg's independent DIFC/guard layer. That layer is separately covered by internal/guard and internal/difc unit tests.
  • Part D/E: gh CLI had no GH_TOKEN in this job, so the token-scope boundary (mcpg read-only MCP + read-only REST token) could not be independently exercised here.

Consistent with the default runtime baseline — read-only guarantee holds identically under gvisor for every reachable probe.

🔒 mcpg read-only stress (gVisor runtime) by Read-Only Stress: gVisor runtime

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant