feat: add integration tests for AWF + MCP Gateway host access#195
feat: add integration tests for AWF + MCP Gateway host access#195
Conversation
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
✅ Coverage Check PassedOverall Coverage
Coverage comparison generated by |
|
@copilot refresh the gateway with the latest release and newest behaviour |
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Updated MCP Gateway to v0.0.48 (latest stable) and added gateway integration tests in 8d7927f. The tests now include:
The workflow also now pulls the latest gateway image before running tests. |
…on-tests-ci # Conflicts: # .github/workflows/test-integration.yml
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
🌑 The shadows whisper... Smoke Codex failed. The oracle requires further meditation... |
| }, | ||
| }, | ||
| }; | ||
| fs.writeFileSync(testConfigPath, JSON.stringify(testConfig, null, 2)); |
Check failure
Code scanning / CodeQL
Insecure temporary file High test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 19 days ago
In general, the fix is to stop constructing temp file paths manually from os.tmpdir() and instead use a library that securely creates a unique file with appropriate permissions, such as tmp. That library creates the file atomically in a safe way and returns its path, eliminating the race condition and permission-issues associated with hand-rolled temp file names.
Concretely here, in tests/integration/mcpg-integration.test.ts, we should:
- Import the
tmplibrary. - Replace the manual construction of
testConfigPathusingos.tmpdir()andDate.now()with a call totmp.fileSync, letting it create the file securely under the hood. - Keep the rest of the logic (writing JSON to that file and later deleting it) unchanged.
Specifically:
- Add
import * as tmp from 'tmp';alongside the other imports at the top oftests/integration/mcpg-integration.test.ts. - Replace the line:
testConfigPath = path.join(tmpDir, \awf-test-config-${Date.now()}.json`);with a secure creation usingtmp.fileSync`, e.g.:const tmpFile = tmp.fileSync({ prefix: 'awf-test-config-', postfix: '.json' });testConfigPath = tmpFile.name;
We keeptmpDirdefined, but no longer use it for the config file path. The rest of the setup and cleanup logic stays as-is.
| @@ -20,6 +20,7 @@ | ||
| import * as fs from 'fs'; | ||
| import * as os from 'os'; | ||
| import * as path from 'path'; | ||
| import * as tmp from 'tmp'; | ||
| import execa = require('execa'); | ||
|
|
||
| // MCP Gateway configuration - use latest stable version | ||
| @@ -40,8 +41,9 @@ | ||
| runner = createRunner(); | ||
| docker = createDockerHelper(); | ||
|
|
||
| // Create a test config file for volume mount tests | ||
| testConfigPath = path.join(tmpDir, `awf-test-config-${Date.now()}.json`); | ||
| // Create a test config file for volume mount tests using a secure temp file | ||
| const tmpFile = tmp.fileSync({ prefix: 'awf-test-config-', postfix: '.json' }); | ||
| testConfigPath = tmpFile.name; | ||
| const testConfig = { | ||
| testKey: 'testValue', | ||
| servers: { |
| @@ -42,7 +42,8 @@ | ||
| "chalk": "^4.1.2", | ||
| "commander": "^12.0.0", | ||
| "execa": "^5.1.1", | ||
| "js-yaml": "^4.1.1" | ||
| "js-yaml": "^4.1.1", | ||
| "tmp": "^0.2.5" | ||
| }, | ||
| "devDependencies": { | ||
| "@commitlint/cli": "^20.1.0", |
| Package | Version | Security advisories |
| tmp (npm) | 0.2.5 | None |
| }, | ||
| }, | ||
| }; | ||
| fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2)); |
Check failure
Code scanning / CodeQL
Insecure temporary file High test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 19 days ago
In general, to fix insecure temporary file creation in Node.js, avoid manually building paths under os.tmpdir() and instead delegate to a library that creates unique, non-world-readable files atomically. The recommended library is tmp, which handles secure file creation and cleanup.
For this specific test in tests/integration/mcpg-integration.test.ts, the best fix is to replace the manual construction of mcpConfigPath (path.join(tmpDir, ...) based on os.tmpdir()) with a secure temporary file created by tmp. We will:
- Add an import for the
tmplibrary near the other imports. - Replace the
const mcpConfigPath = path.join(tmpDir, ...)line with a call totmp.fileSyncthat:- uses a prefix like
'mcp-gateway-config-' - uses a
.jsonpostfix - sets
dir: tmpDirto keep the file in the same temp directory (so the existingvolumeMountsstill work).
- uses a prefix like
- Store the
tmpfile handle (tmpFile) so we can both access.name(the path) and call.removeCallback()infinallyfor cleanup. - Retain the existing
fs.writeFileSync(mcpConfigPath, ...)and the DockervolumeMountslogic, but in thefinallyblock, instead of only unlinking the path, prefertmpFile.removeCallback()(with a fallbackunlinkSyncif needed).
These changes are all within the shown test file and do not change the behavior visible to the rest of the code: the test still mounts the same temp directory and reads the config file, but the file is now created securely.
| @@ -20,6 +20,7 @@ | ||
| import * as fs from 'fs'; | ||
| import * as os from 'os'; | ||
| import * as path from 'path'; | ||
| import * as tmp from 'tmp'; | ||
| import execa = require('execa'); | ||
|
|
||
| // MCP Gateway configuration - use latest stable version | ||
| @@ -418,8 +419,14 @@ | ||
| test('MCP Gateway config can be mounted into AWF container', async () => { | ||
| await startMcpGateway(); | ||
|
|
||
| // Create MCP client config file | ||
| const mcpConfigPath = path.join(tmpDir, `mcp-gateway-config-${Date.now()}.json`); | ||
| // Create MCP client config file securely in the temp directory | ||
| const tmpFile = tmp.fileSync({ | ||
| dir: tmpDir, | ||
| prefix: 'mcp-gateway-config-', | ||
| postfix: '.json', | ||
| discardDescriptor: true, | ||
| }); | ||
| const mcpConfigPath = tmpFile.name; | ||
| const mcpConfig = { | ||
| mcpServers: { | ||
| 'github-gateway': { | ||
| @@ -450,8 +457,12 @@ | ||
| expect(result.stdout).toContain('github-gateway'); | ||
| expect(result.stdout).toContain(`host.docker.internal:${MCPG_HOST_PORT}`); | ||
| } finally { | ||
| if (fs.existsSync(mcpConfigPath)) { | ||
| fs.unlinkSync(mcpConfigPath); | ||
| try { | ||
| tmpFile.removeCallback(); | ||
| } catch { | ||
| if (fs.existsSync(mcpConfigPath)) { | ||
| fs.unlinkSync(mcpConfigPath); | ||
| } | ||
| } | ||
| } | ||
| }, 180000); |
| @@ -42,7 +42,8 @@ | ||
| "chalk": "^4.1.2", | ||
| "commander": "^12.0.0", | ||
| "execa": "^5.1.1", | ||
| "js-yaml": "^4.1.1" | ||
| "js-yaml": "^4.1.1", | ||
| "tmp": "^0.2.5" | ||
| }, | ||
| "devDependencies": { | ||
| "@commitlint/cli": "^20.1.0", |
| Package | Version | Security advisories |
| tmp (npm) | 0.2.5 | None |
Smoke Test Results (Claude)Last 2 Merged PRs:
Test Results:
Overall Status: PASS
|
Smoke Test ResultsLast 2 Merged PRs:
Test Results:
Status: PASS cc @Mossaka (assignee)
|
Adds integration tests to verify AWF's host access features required for MCP Gateway integration, including
--enable-host-access, environment variable passthrough, and volume mounts.Changes
tests/fixtures/awf-runner.ts: ExtendedAwfOptionswith:enableHostAccess- enables--enable-host-accessflagenvAll- enables--env-allflag--env KEY=VALUEflag support for individual env varstests/integration/mcpg-integration.test.ts: 15 tests covering:--envand--env-all)ghcr.io/githubnext/gh-aw-mcpg:v0.0.48).github/workflows/test-integration.yml: Addedtest-mcpg-integrationjob with:ghcr.io/githubnext/gh-aw-mcpg:v0.0.48)Usage
Original prompt
Add integration tests / CI to test the integration of awf (firewall) and gh-aw-mcpg. Reference doc below:
Integrating gh-aw-mcpg with gh-aw-firewall
This guide explains how to run an MCP gateway as a Docker container on the host and connect to it from within an AWF (Agentic Workflow Firewall) container.
Tested versions:
ghcr.io/githubnext/gh-aw-mcpg:v0.0.10gh-aw-firewall v0.8.2Architecture
Prerequisites
ghcr.io/githubnext/gh-aw-mcpg:v0.0.10or laterStep 1: Start the MCP Gateway Container
The MCP Gateway is available as a Docker image that includes default MCP servers (github, fetch, memory).
Note: The container runs on port 8000 internally, mapped to port 80 on the host. It needs Docker socket access to spawn MCP server containers.
Verify it's running:
curl http://127.0.0.1:80/health # Should return: OKOptional: Custom Gateway Configuration
If you need to customize the MCP servers, create
/tmp/mcpg-config.json:{ "mcpServers": { "github": { "type": "local", "container": "ghcr.io/github/github-mcp-server:latest", "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "" } } } }Then mount it when starting the container:
Step 2: Create MCP Client Configuration for Copilot
Create
/tmp/mcp-gateway-config.json:{ "mcpServers": { "github-gateway": { "type": "http", "url": "http://host.docker.internal/mcp/github", "headers": { "Authorization": "Bearer my-session-token" }, "tools": ["*"] } } }Key points:
type: "http"- Connect via HTTP instead of spawning a local processurluseshost.docker.internal- Docker's special hostname to reach the hostheaders- Pass Bearer token for gateway authentication (can be any string for session identification)Step 3: Run AWF with Copilot CLI