From 704072b748e4e09f97aace00f232daecaa2e37b8 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Wed, 1 Apr 2026 08:48:24 -0700 Subject: [PATCH] feat: include api-proxy token logs in firewall audit artifact Move api-proxy logs from a sibling directory of proxyLogsDir to a subdirectory inside it. When --proxy-logs-dir is set (e.g., by gh-aw to /tmp/gh-aw/sandbox/firewall/logs), the api-proxy logs now land at .../logs/api-proxy-logs/ instead of .../api-proxy-logs/. This means the existing firewall-audit-logs artifact upload automatically captures token-usage.jsonl without any workflow changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/docker-manager.test.ts | 16 ++++++++-------- src/docker-manager.ts | 14 ++++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts index 42be910f2..a3743f78f 100644 --- a/src/docker-manager.test.ts +++ b/src/docker-manager.test.ts @@ -1759,7 +1759,7 @@ describe('docker-manager', () => { expect(squid.volumes).toContain('/tmp/awf-test/squid-logs:/var/log/squid:rw'); }); - it('should use sibling api-proxy-logs directory when proxyLogsDir is specified', () => { + it('should use api-proxy-logs subdirectory inside proxyLogsDir when specified', () => { const config: WrapperConfig = { ...mockConfig, proxyLogsDir: '/custom/proxy/logs', @@ -1772,7 +1772,7 @@ describe('docker-manager', () => { }); const apiProxy = result.services['api-proxy']; - expect(apiProxy.volumes).toContain('/custom/proxy/api-proxy-logs:/var/log/api-proxy:rw'); + expect(apiProxy.volumes).toContain('/custom/proxy/logs/api-proxy-logs:/var/log/api-proxy:rw'); }); it('should use workDir/api-proxy-logs when proxyLogsDir is not specified', () => { @@ -2711,7 +2711,7 @@ describe('docker-manager', () => { expect(fs.existsSync(proxyLogsDir)).toBe(true); }); - it('should create api-proxy-logs sibling directory when proxyLogsDir is specified', async () => { + it('should create api-proxy-logs subdirectory inside proxyLogsDir when specified', async () => { const proxyLogsDir = path.join(testDir, 'custom-proxy-logs'); const config: WrapperConfig = { allowedDomains: ['github.com'], @@ -2728,8 +2728,8 @@ describe('docker-manager', () => { // May fail after writing configs } - // Verify api-proxy-logs sibling directory was created - const apiProxyLogsDir = path.join(testDir, 'api-proxy-logs'); + // Verify api-proxy-logs subdirectory was created inside proxyLogsDir + const apiProxyLogsDir = path.join(proxyLogsDir, 'api-proxy-logs'); expect(fs.existsSync(apiProxyLogsDir)).toBe(true); }); @@ -3253,11 +3253,11 @@ describe('docker-manager', () => { } }); - it('should chmod api-proxy-logs sibling when proxyLogsDir is specified', async () => { + it('should chmod api-proxy-logs subdirectory when proxyLogsDir is specified', async () => { // proxyLogsDir must be OUTSIDE workDir since cleanup deletes workDir const externalDir = fs.mkdtempSync(path.join(os.tmpdir(), 'awf-proxy-logs-test-')); const proxyLogsDir = path.join(externalDir, 'proxy-logs'); - const apiProxyLogsDir = path.join(externalDir, 'api-proxy-logs'); + const apiProxyLogsDir = path.join(proxyLogsDir, 'api-proxy-logs'); fs.mkdirSync(proxyLogsDir, { recursive: true }); fs.mkdirSync(apiProxyLogsDir, { recursive: true }); fs.writeFileSync(path.join(proxyLogsDir, 'access.log'), 'proxy log content'); @@ -3266,7 +3266,7 @@ describe('docker-manager', () => { try { await cleanup(testDir, false, proxyLogsDir); - // Verify chmod was called on both proxyLogsDir and api-proxy-logs sibling + // Verify chmod was called on both proxyLogsDir and api-proxy-logs subdirectory expect(mockExecaSync).toHaveBeenCalledWith('chmod', ['-R', 'a+rX', proxyLogsDir]); expect(mockExecaSync).toHaveBeenCalledWith('chmod', ['-R', 'a+rX', apiProxyLogsDir]); } finally { diff --git a/src/docker-manager.ts b/src/docker-manager.ts index a5a3ef898..51b36c7b8 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -366,10 +366,11 @@ export function generateDockerCompose( // Squid logs path: use proxyLogsDir if specified (direct write), otherwise workDir/squid-logs const squidLogsPath = config.proxyLogsDir || `${config.workDir}/squid-logs`; - // API proxy logs path: if proxyLogsDir is specified, write to sibling directory + // API proxy logs path: if proxyLogsDir is specified, write inside it as a subdirectory + // so that token-usage.jsonl is included in the firewall-audit-logs artifact automatically. // Otherwise, write to workDir/api-proxy-logs (will be moved to /tmp after cleanup) const apiProxyLogsPath = config.proxyLogsDir - ? path.join(path.dirname(config.proxyLogsDir), 'api-proxy-logs') + ? path.join(config.proxyLogsDir, 'api-proxy-logs') : path.join(config.workDir, 'api-proxy-logs'); // Build Squid volumes list @@ -1598,11 +1599,12 @@ export async function writeConfigs(config: WrapperConfig): Promise { logger.debug(`Squid logs directory created at: ${squidLogsDir}`); // Create api-proxy logs directory for persistence - // If proxyLogsDir is specified, write to sibling directory (timeout-safe) + // If proxyLogsDir is specified, write inside it as a subdirectory (timeout-safe, + // and included in the firewall-audit-logs artifact upload automatically) // Otherwise, write to workDir/api-proxy-logs (will be moved to /tmp after cleanup) // Note: API proxy runs as user 'apiproxy' (non-root) const apiProxyLogsDir = config.proxyLogsDir - ? path.join(path.dirname(config.proxyLogsDir), 'api-proxy-logs') + ? path.join(config.proxyLogsDir, 'api-proxy-logs') : path.join(config.workDir, 'api-proxy-logs'); if (!fs.existsSync(apiProxyLogsDir)) { fs.mkdirSync(apiProxyLogsDir, { recursive: true, mode: 0o777 }); @@ -2146,9 +2148,9 @@ export async function cleanup(workDir: string, keepFiles: boolean, proxyLogsDir? // Preserve api-proxy logs before cleanup if (proxyLogsDir) { - // Logs were written directly to sibling of proxyLogsDir during runtime (timeout-safe) + // Logs were written inside proxyLogsDir/api-proxy-logs during runtime (timeout-safe) // Just fix permissions so they're readable - const apiProxyLogsDir = path.join(path.dirname(proxyLogsDir), 'api-proxy-logs'); + const apiProxyLogsDir = path.join(proxyLogsDir, 'api-proxy-logs'); if (fs.existsSync(apiProxyLogsDir)) { try { execa.sync('chmod', ['-R', 'a+rX', apiProxyLogsDir]);