Skip to content

Consolidate delegation binding tuple mapping - #12603

Open
lpcox with Copilot wants to merge 5 commits into
mainfrom
copilot/duplicate-code-fix
Open

Consolidate delegation binding tuple mapping#12603
lpcox with Copilot wants to merge 5 commits into
mainfrom
copilot/duplicate-code-fix

Conversation

Copilot AI commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

The delegation binding tuple was duplicated across identity creation, idempotency checks, and restart recovery, creating drift risk when fields change. This refactor establishes one shared representation for consistent binding semantics.

  • Shared binding model
    • Added delegationBinding for the nine security-relevant fields.
    • Centralized request extraction, comparison, and reconstruction.
  • Refactored call sites
    • Identity creation stores the shared binding.
    • bindingEquals compares shared bindings.
    • Recovery validation reconstructs requests through Identity.toRequest().
  • Regression coverage
    • Added round-trip coverage for binding fields, invocation expiry, idempotency key, and computed TTL.
func (id *Identity) toRequest() CreateOrConfirmRequest {
    return id.delegationBinding.toRequest(
        id.ExpiresAt.Sub(id.CreatedAt),
        id.IdempotencyKey,
    )
}

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix duplicate code pattern in identity field mapping Consolidate delegation binding tuple mapping Sep 6, 2026
Copilot AI requested a review from lpcox September 6, 2026 19:06
@lpcox
lpcox marked this pull request as ready for review September 6, 2026 22:11
Copilot AI balanced review requested due to automatic review settings September 6, 2026 22:11

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.

🟡 Changes recommended

The binding comparison must include future fields automatically to resolve the remaining drift risk.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Centralizes delegation binding semantics across identity creation, idempotency checks, and recovery.

Changes:

  • Adds a shared delegationBinding model.
  • Refactors creation, comparison, and reconstruction.
  • Adds round-trip regression coverage.
File summaries
File Review
internal/delegation/store.go Stores requests using the shared binding.
internal/delegation/recovery.go Reconstructs recovery requests through Identity.toRequest().
internal/delegation/identity.go Defines binding helpers; comparison still manually enumerates fields, leaving drift risk.
internal/delegation/identity_test.go Adds round-trip coverage, but the assertion can miss matching helper omissions.
Review details

Suppressed comments (1)

internal/delegation/identity_test.go:24

  • This round-trip assertion reuses both helpers under test on each side, so matching omissions in bindingFromRequest and equals still pass. Compare the reconstructed request directly with the original request (after replacing the computed TTL); that independently verifies every populated request field, including invocation expiry and the idempotency key.
	restored := identity.toRequest()
	require.Equal(t, req.IdempotencyKey, restored.IdempotencyKey)
	assert.True(t, bindingFromRequest(req).equals(bindingFromRequest(restored)))
	assert.Equal(t, identity.ExpiresAt.Sub(identity.CreatedAt), restored.RequestedTTL)
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Balanced

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

Comment thread internal/delegation/identity.go Outdated
Comment on lines +70 to +80
func (b delegationBinding) equals(other delegationBinding) bool {
return b.RunID == other.RunID &&
b.EnclaveBackend == other.EnclaveBackend &&
b.EnclaveEntryID == other.EnclaveEntryID &&
b.InvocationID == other.InvocationID &&
b.Repository == other.Repository &&
b.ToolPolicy == other.ToolPolicy &&
b.SchemaHash == other.SchemaHash &&
b.AdmittedDefaultBranchSHA == other.AdmittedDefaultBranchSHA &&
b.InvocationExpiresAt.Equal(other.InvocationExpiresAt)
}
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox

lpcox commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback and fix failing ci lint check https://github.com/github/gh-aw-mcpg/actions/runs/34053570930/job/101567564808?pr=12603

@github-actions

This comment has been minimized.

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

Copilot AI commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback and fix failing ci lint check https://github.com/github/gh-aw-mcpg/actions/runs/34053570930/job/101567564808?pr=12603

Addressed in cc7a407. Binding comparisons now normalize timestamps and compare the complete binding struct; the staticcheck lint failure is resolved. make agent-finished passes.

@lpcox

lpcox commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

@copilot resolve merge conflicts and rebase to main

…e-fix

# Conflicts:
#	internal/delegation/identity.go
#	internal/delegation/store.go

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

🔒 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 (list_issues, list_pull_requests, get_file_contents, list_commits) data returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) all 7 target tools absent from the 23-tool catalog (only read tools exposed) BLOCKED ⚠️
C CLI reads via github proxy CLI on PATH data returned ALLOWED
D CLI REST writes gh not authenticated — could not test BLOCKED ⚠️
E CLI GraphQL mutations gh not authenticated — could not test BLOCKED ⚠️

Overall: INCONCLUSIVE

Notes:

  • Part B: the gateway/backend only exposed 23 read-only tools; add_issue_comment, star_repository, issue_write, create_branch, create_or_update_file, create_pull_request were never listed in the catalog. Per gh-aw's tools.github: frontmatter, the backend always launches with GITHUB_READ_ONLY=1, so absence here reflects backend/toolset configuration, not independently-confirmed gateway (mcpg) enforcement. No write attempt was possible or made.
  • Part D/E: gh auth status reported no authenticated host, so token-scope write rejection could not be exercised. No write attempt was made.
  • No reaction, star, issue, comment, branch, file, or PR was created during this run.

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

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — gVisor

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

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 (reaction/star/issue/comment/branch/file/PR) no write tools present in catalog (23 read-only tools only) BLOCKED ⚠️
C CLI reads via github CLI proxy data returned ALLOWED
D CLI REST writes gh not authenticated, could not test BLOCKED ⚠️
E CLI GraphQL mutations gh not authenticated, could not test BLOCKED ⚠️

Overall: INCONCLUSIVE

Notes:

  • Parts A and C fully passed — reads work identically to other runtimes.
  • Part B: the gateway-exposed GitHub tool catalog contains only 23 read-only tools (no add_issue_comment, star_repository, issue_write, create_branch, create_or_update_file, create_pull_request). This is consistent with gh-aw's tools.github: frontmatter always launching the backend with GITHUB_READ_ONLY=1 — writes never reach a write-capable backend, so gateway-level enforcement (vs. backend config) could not be independently confirmed on this surface.
  • Parts D/E: gh auth status reported no authenticated host in this environment, so REST/GraphQL write attempts could not be executed to test the token-scope boundary.
  • No writes leaked in any part of this test on the gVisor runtime.

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

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — docker-sbx

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: docker-sbx (KVM-isolated microVM)

Part Surface Op Result Expected Status
A MCP reads (issues, PRs, README.md, commits) data returned ALLOWED
B MCP writes (add_issue_comment/star_repository/issue_write/create_branch/create_or_update_file/create_pull_request) absent from 23-tool catalog — never attempted BLOCKED ⚠️
C CLI reads (list_issues, get_file_contents) via proxied github CLI data returned ALLOWED
D CLI REST writes (reaction/star/issue/comment) gh unauthenticated (GH_TOKEN invalid) BLOCKED ⚠️
E CLI GraphQL mutations (addReaction/addStar/createIssue) gh unauthenticated (GH_TOKEN invalid) BLOCKED ⚠️

Overall: INCONCLUSIVE

Notes:

  • The exposed MCP tool catalog contains only 23 read-only tools (get_commit, get_file_contents, list_issues, list_pull_requests, list_commits, search_*, etc.) — no write tools (add_issue_comment, star_repository, issue_write, create_branch, create_or_update_file, create_pull_request) are registered at all, consistent with gh-aw's tools.github: always forcing GITHUB_READ_ONLY=1. Since no write tool was ever callable, Part B cannot independently confirm mcpg's own gateway-level DIFC/guard blocking (see internal/guard/internal/difc unit tests for that evidence) — recorded as INCONCLUSIVE per the test's own criteria, not a FAIL.
  • gh auth status reports the GH_TOKEN is invalid in this environment, so Parts D and E (proxied CLI REST writes and GraphQL mutations) could not be attempted — recorded as INCONCLUSIVE, not PASS/FAIL.
  • No write of any kind (reaction, star, issue, comment, branch, file, PR) was created or leaked in this run.

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

Copilot AI commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback and fix failing ci lint check https://github.com/github/gh-aw-mcpg/actions/runs/34053570930/job/101567564808?pr=12603

Fixed the complete comparable binding comparison, preserved requested TTL recovery, and resolved the reported staticcheck lint issue. make agent-finished passes; commit b341676.

@copilot resolve merge conflicts and rebase to main

Merged main and resolved the delegation identity/store conflicts. The merge commit is 404f151, with the follow-up fixes in b341676.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code Pattern: Repeated Identity/CreateOrConfirmRequest Field Mapping

3 participants