Skip to content

Commit f0a8321

Browse files
Copilotpelikhan
andauthored
feat: update DIFC proxying to also proxy actions/github-script (#22712)
- Set GITHUB_API_URL, GITHUB_GRAPHQL_URL, and NODE_EXTRA_CA_CERTS in start_difc_proxy.sh so Octokit calls (actions/github-script) are routed through the proxy alongside gh CLI calls - Restore those variables in stop_difc_proxy.sh - Wrap qmd indexing steps with proxy start/stop in buildQmdIndexingJob when DIFC guards are configured - Update compiler_difc_proxy.go package docs and add tests Agent-Logs-Url: https://github.com/github/gh-aw/sessions/10a7cf43-239c-4be2-9599-37abebe341cf Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Co-authored-by: Peli de Halleux <pelikhan@users.noreply.github.com>
1 parent 78171cb commit f0a8321

6 files changed

Lines changed: 112 additions & 11 deletions

File tree

.github/workflows/weekly-blog-post-writer.lock.yml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

actions/setup/sh/start_difc_proxy.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,17 @@ for i in $(seq 1 30); do
6666
fi
6767
if curl -sf "https://localhost:18443/api/v3/health" -o /dev/null 2>/dev/null; then
6868
echo "DIFC proxy ready on port 18443"
69+
# Route gh CLI calls through the proxy.
6970
echo "GH_HOST=localhost:18443" >> "$GITHUB_ENV"
7071
git remote add proxy "https://localhost:18443/${GITHUB_REPOSITORY}.git" || true
72+
# Route actions/github-script Octokit calls through the proxy.
73+
# Save the originals so stop_difc_proxy.sh can restore them.
74+
echo "GH_AW_ORIGINAL_GITHUB_API_URL=${GITHUB_API_URL:-}" >> "$GITHUB_ENV"
75+
echo "GH_AW_ORIGINAL_GITHUB_GRAPHQL_URL=${GITHUB_GRAPHQL_URL:-}" >> "$GITHUB_ENV"
76+
echo "GITHUB_API_URL=https://localhost:18443/api/v3" >> "$GITHUB_ENV"
77+
echo "GITHUB_GRAPHQL_URL=https://localhost:18443/api/graphql" >> "$GITHUB_ENV"
78+
# Trust the proxy TLS certificate from Node.js (used by actions/github-script).
79+
echo "NODE_EXTRA_CA_CERTS=$PROXY_LOG_DIR/proxy-tls/ca.crt" >> "$GITHUB_ENV"
7180
PROXY_READY=true
7281
break
7382
fi

actions/setup/sh/stop_difc_proxy.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,16 @@ fi
2727
if [ "${GH_HOST:-}" = "localhost:18443" ]; then
2828
echo "GH_HOST=" >> "$GITHUB_ENV"
2929
fi
30+
31+
# Restore GITHUB_API_URL and GITHUB_GRAPHQL_URL to their original values.
32+
if [ "${GITHUB_API_URL:-}" = "https://localhost:18443/api/v3" ]; then
33+
echo "GITHUB_API_URL=${GH_AW_ORIGINAL_GITHUB_API_URL:-}" >> "$GITHUB_ENV"
34+
fi
35+
if [ "${GITHUB_GRAPHQL_URL:-}" = "https://localhost:18443/api/graphql" ]; then
36+
echo "GITHUB_GRAPHQL_URL=${GH_AW_ORIGINAL_GITHUB_GRAPHQL_URL:-}" >> "$GITHUB_ENV"
37+
fi
38+
39+
# Clear the Node.js CA certs override set by start_difc_proxy.sh.
40+
echo "NODE_EXTRA_CA_CERTS=" >> "$GITHUB_ENV"
41+
3042
echo "DIFC proxy stopped"

pkg/workflow/compiler_difc_proxy.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,33 @@ package workflow
1515
// is running. Only gh CLI calls that honour GH_HOST are actually filtered.
1616
//
1717
// Note: qmd indexing GitHub API calls are made via actions/github-script (@actions/github
18-
// Octokit), which uses GITHUB_API_URL / GITHUB_GRAPHQL_URL rather than GH_HOST. The proxy
19-
// does not intercept those calls, so no proxy wrapping is injected into the qmd indexing job.
18+
// Octokit). The proxy sets GITHUB_API_URL, GITHUB_GRAPHQL_URL, and NODE_EXTRA_CA_CERTS in
19+
// addition to GH_HOST, so it intercepts Octokit calls as well. Proxy wrapping is therefore
20+
// also injected around qmd indexing steps when DIFC guards are configured.
2021
//
2122
// The proxy uses the same container image as the MCP gateway (gh-aw-mcpg)
2223
// but runs in "proxy" mode with --guards-mode filter (graceful degradation)
2324
// and --tls (required by the gh CLI HTTPS-only constraint).
2425
//
2526
// Injection conditions:
2627
//
27-
// Main job: GitHub tool has explicit guard policies (min-integrity set) AND
28-
// custom steps set GH_TOKEN
28+
// Main job: GitHub tool has explicit guard policies (min-integrity set) AND
29+
// custom steps set GH_TOKEN
30+
// Indexing job: GitHub tool has explicit guard policies (min-integrity set)
2931
//
3032
// Proxy lifecycle within the main job:
3133
// 1. Start proxy — after "Configure gh CLI" step, before custom steps
32-
// 2. Custom steps run with GH_HOST=localhost:18443 (set via $GITHUB_ENV)
34+
// 2. Custom steps run with GH_HOST=localhost:18443, GITHUB_API_URL, GITHUB_GRAPHQL_URL,
35+
// and NODE_EXTRA_CA_CERTS set (via $GITHUB_ENV)
3336
// 3. Stop proxy — before MCP gateway starts (generateMCPSetup); always runs
3437
// even if earlier steps failed (if: always(), continue-on-error: true)
3538
//
39+
// Proxy lifecycle within the indexing job:
40+
// 1. Start proxy — before qmd index-building steps
41+
// 2. qmd steps run with all proxy env vars set (GH_HOST, GITHUB_API_URL, GITHUB_GRAPHQL_URL,
42+
// NODE_EXTRA_CA_CERTS); Octokit calls in actions/github-script are intercepted
43+
// 3. Stop proxy — after qmd steps; always runs (if: always(), continue-on-error: true)
44+
//
3645
// Guard policy note:
3746
//
3847
// The proxy policy uses only the static fields from the workflow's frontmatter

pkg/workflow/compiler_difc_proxy_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,4 +579,57 @@ func TestDIFCProxyInjectedInIndexingJob(t *testing.T) {
579579
assert.Contains(t, stopStep, "Stop DIFC proxy")
580580
assert.Contains(t, stopStep, "stop_difc_proxy.sh")
581581
})
582+
583+
t.Run("buildQmdIndexingJob wraps steps with proxy when guard policy configured", func(t *testing.T) {
584+
data := &WorkflowData{
585+
Tools: map[string]any{
586+
"github": map[string]any{"min-integrity": "approved"},
587+
},
588+
QmdConfig: &QmdToolConfig{
589+
Searches: []*QmdSearchEntry{{Query: "repo:owner/repo language:Markdown"}},
590+
CacheKey: "qmd-test",
591+
},
592+
SandboxConfig: &SandboxConfig{},
593+
}
594+
ensureDefaultMCPGatewayConfig(data)
595+
596+
job, err := c.buildQmdIndexingJob(data)
597+
require.NoError(t, err, "buildQmdIndexingJob should succeed")
598+
require.NotNil(t, job, "job should not be nil")
599+
600+
allSteps := strings.Join(job.Steps, "\n")
601+
require.Contains(t, allSteps, "Start DIFC proxy for pre-agent gh calls",
602+
"indexing job should include proxy start step when guard policy is configured")
603+
require.Contains(t, allSteps, "Stop DIFC proxy",
604+
"indexing job should include proxy stop step when guard policy is configured")
605+
606+
// Proxy start must come before the qmd index step and proxy stop must come after.
607+
startIdx := strings.Index(allSteps, "Start DIFC proxy for pre-agent gh calls")
608+
stopIdx := strings.Index(allSteps, "Stop DIFC proxy")
609+
assert.Less(t, startIdx, stopIdx, "Start proxy must come before Stop proxy in indexing job")
610+
})
611+
612+
t.Run("buildQmdIndexingJob has no proxy steps without guard policy", func(t *testing.T) {
613+
data := &WorkflowData{
614+
Tools: map[string]any{
615+
"github": map[string]any{"toolsets": []string{"default"}},
616+
},
617+
QmdConfig: &QmdToolConfig{
618+
Searches: []*QmdSearchEntry{{Query: "repo:owner/repo language:Markdown"}},
619+
CacheKey: "qmd-test",
620+
},
621+
SandboxConfig: &SandboxConfig{},
622+
}
623+
ensureDefaultMCPGatewayConfig(data)
624+
625+
job, err := c.buildQmdIndexingJob(data)
626+
require.NoError(t, err, "buildQmdIndexingJob should succeed")
627+
require.NotNil(t, job, "job should not be nil")
628+
629+
allSteps := strings.Join(job.Steps, "\n")
630+
assert.NotContains(t, allSteps, "Start DIFC proxy",
631+
"indexing job should NOT include proxy start step without guard policy")
632+
assert.NotContains(t, allSteps, "Stop DIFC proxy",
633+
"indexing job should NOT include proxy stop step without guard policy")
634+
})
582635
}

pkg/workflow/qmd.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -591,14 +591,23 @@ func (c *Compiler) buildQmdIndexingJob(data *WorkflowData) (*Job, error) {
591591
// Generate all qmd index-building steps (cache restore/save, Node.js, SDK install, github-script).
592592
qmdSteps := generateQmdIndexSteps(data.QmdConfig)
593593

594-
// Note: qmd indexing GitHub API calls are made via actions/github-script (@actions/github
595-
// Octokit), which uses GITHUB_API_URL / GITHUB_GRAPHQL_URL rather than GH_HOST.
596-
// The DIFC proxy started by buildStartDIFCProxyStepYAML() only sets GH_HOST, so it does
597-
// not intercept qmd's traffic. We therefore do not wrap qmd indexing steps with the proxy.
594+
// Wrap qmd indexing steps with the DIFC proxy when guard policies are configured.
595+
// The proxy now sets GITHUB_API_URL, GITHUB_GRAPHQL_URL, and NODE_EXTRA_CA_CERTS in
596+
// addition to GH_HOST, so it intercepts Octokit calls made by actions/github-script
597+
// during qmd indexing.
598598
if hasDIFCGuardsConfigured(data) {
599-
qmdLog.Print("DIFC guards configured; qmd indexing steps are not wrapped with GH_HOST-based DIFC proxy")
599+
qmdLog.Print("DIFC guards configured; wrapping qmd indexing steps with DIFC proxy")
600+
startStep := c.buildStartDIFCProxyStepYAML(data)
601+
if startStep != "" {
602+
steps = append(steps, startStep)
603+
steps = append(steps, qmdSteps...)
604+
steps = append(steps, buildStopDIFCProxyStepYAML())
605+
} else {
606+
steps = append(steps, qmdSteps...)
607+
}
608+
} else {
609+
steps = append(steps, qmdSteps...)
600610
}
601-
steps = append(steps, qmdSteps...)
602611

603612
// The indexing job runs after the activation job to inherit the artifact prefix output.
604613
needs := []string{string(constants.ActivationJobName)}

0 commit comments

Comments
 (0)