From 24e648ec51679d298d3f3b116363042c10ce008c Mon Sep 17 00:00:00 2001 From: "Jiaxiao (mossaka) Zhou" Date: Wed, 25 Feb 2026 20:48:12 +0000 Subject: [PATCH] refactor: remove --allow-full-filesystem-access flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flag was an escape hatch that disabled selective mounting security by adding a blanket /:/host:rw mount, exposing all credential files. It contradicts the security model and is no longer needed: - gh-aw never passes this flag (not in awf_helpers.go) - No CI workflows or smoke tests use it - It was already hidden from --help in PR #1035 - The --mount flag covers mounting specific directories Credential hiding is now unconditional — the if/else guards around selective mounting are removed, making the secure path the only path. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../content/docs/reference/cli-reference.md | 26 ---- docs/selective-mounting.md | 18 +-- docs/test-analysis/chroot.md | 2 - docs/test-analysis/protocol-security.md | 3 - docs/test-analysis/test-infra.md | 1 - docs/usage.md | 6 - src/cli.ts | 11 -- src/docker-manager.test.ts | 28 ---- src/docker-manager.ts | 131 ++++++++---------- src/types.ts | 40 ------ tests/fixtures/awf-runner.ts | 11 -- tests/integration/credential-hiding.test.ts | 47 ------- 12 files changed, 60 insertions(+), 264 deletions(-) diff --git a/docs-site/src/content/docs/reference/cli-reference.md b/docs-site/src/content/docs/reference/cli-reference.md index 6e2604f0f..c02fad7ba 100644 --- a/docs-site/src/content/docs/reference/cli-reference.md +++ b/docs-site/src/content/docs/reference/cli-reference.md @@ -42,7 +42,6 @@ awf [options] -- | `--enable-host-access` | flag | `false` | Enable access to host services via host.docker.internal | | `--allow-host-ports ` | string | `80,443` | Ports to allow when using --enable-host-access | | `--agent-image ` | string | `default` | Agent container image (default, act, or custom) | -| `--allow-full-filesystem-access` | flag | `false` | ⚠️ Mount entire host filesystem with read-write access | | `-V, --version` | flag | — | Display version | | `-h, --help` | flag | — | Display help | @@ -364,31 +363,6 @@ Custom images are validated against approved patterns to prevent supply chain at **See also:** [Agent Images Reference](/gh-aw-firewall/reference/agent-images/) -### `--allow-full-filesystem-access` - -:::danger[⚠️ SECURITY WARNING] -This flag **DISABLES selective mounting security** and mounts the entire host filesystem with **read-write access**. This exposes **ALL** credential files including: - -- Docker Hub tokens (`~/.docker/config.json`) -- GitHub CLI tokens (`~/.config/gh/hosts.yml`) -- NPM, Cargo, Composer credentials -- SSH keys, GPG keys, and other sensitive files - -**Only use this flag if:** -1. You are running trusted code that you have fully reviewed -2. You understand the security implications -3. You cannot use `--mount` to selectively mount needed directories -::: - -```bash -# ⚠️ Use with extreme caution -sudo awf --allow-full-filesystem-access \ - --allow-domains github.com \ - -- trusted-command -``` - -**Alternatives:** -- Use `--mount` to selectively mount only needed directories (recommended) ## Exit Codes diff --git a/docs/selective-mounting.md b/docs/selective-mounting.md index 4865ae5d6..c6ace4901 100644 --- a/docs/selective-mounting.md +++ b/docs/selective-mounting.md @@ -179,17 +179,6 @@ sudo awf \ my-command ``` -### Full Filesystem Access (Not Recommended) - -```bash -# ⚠️ Only use if absolutely necessary -sudo awf --allow-full-filesystem-access --allow-domains github.com -- my-command - -# You'll see security warnings: -# ⚠️ SECURITY WARNING: Full filesystem access enabled -# The entire host filesystem is mounted with read-write access -# This exposes sensitive credential files to potential prompt injection attacks -``` ## Comparison: Before vs After @@ -289,7 +278,7 @@ docker inspect awf-agent --format '{{json .Mounts}}' | jq # - /tmp mounted # - $HOME mounted # - /dev/null mounted over credential files -# - NO /:/host mount (unless --allow-full-filesystem-access used) +# - NO /:/host mount ``` ## Migration Guide @@ -319,14 +308,11 @@ awf --allow-domains github.com -- cat /etc/custom/config.json # ✓ New: Use explicit mount awf --mount /etc/custom:/etc/custom:ro --allow-domains github.com -- cat /etc/custom/config.json - -# Or as last resort (not recommended): -awf --allow-full-filesystem-access --allow-domains github.com -- cat /etc/custom/config.json ``` ## Security Best Practices -1. **Default to selective mounting** - Never use `--allow-full-filesystem-access` unless absolutely necessary +1. **Default to selective mounting** - The default behavior provides the best security 2. **Use read-only mounts** - When using `--mount`, prefer `:ro` for directories that don't need writes: ```bash diff --git a/docs/test-analysis/chroot.md b/docs/test-analysis/chroot.md index 6c81e3779..e0313615d 100644 --- a/docs/test-analysis/chroot.md +++ b/docs/test-analysis/chroot.md @@ -283,7 +283,6 @@ The agent container mounts the host filesystem at `/host`, then calls `chroot /h 5. **No large output test** - No test for commands producing large stdout/stderr, which could test buffer handling. -6. **No `--allow-full-filesystem-access` test** - The escape hatch flag is never tested in chroot integration tests. 7. **No credential hiding test** - The selective mounting hides credential files via `/dev/null` overlays, but no test verifies that `cat ~/.docker/config.json` or `cat ~/.ssh/id_rsa` returns empty/fails. @@ -419,7 +418,6 @@ Without the dynamic proc mount: 3. **No cleanup verification** - `entrypoint.sh` has extensive cleanup logic (resolv.conf restoration, hosts file cleanup, script file deletion). None of this is tested. -4. **Missing `--allow-full-filesystem-access` tests** - The escape hatch that mounts `/:/host:rw` instead of selective mounts is never tested. 5. **No `--mount` custom volume test** - Custom volume mounts passed via `--mount` flag are never tested in chroot context. diff --git a/docs/test-analysis/protocol-security.md b/docs/test-analysis/protocol-security.md index e8ab84078..a4349c913 100644 --- a/docs/test-analysis/protocol-security.md +++ b/docs/test-analysis/protocol-security.md @@ -178,8 +178,6 @@ The API proxy sidecar is the core credential isolation mechanism. In production | **Chroot mode debug logs** | Verifies chroot-specific credential hiding log messages. | | **Chroot bypass prevention** | Critical security test: verifies credentials hidden at direct `$HOME` path (not just `/host` path). Previously a bypass vulnerability. | | **Chroot GitHub CLI tokens hidden at direct path** | Same bypass prevention for `hosts.yml`. | -| **Full filesystem access security warnings** | With `--allow-full-filesystem-access`, verifies security warnings are displayed. | -| **Full access: Docker config NOT hidden** | With full access flag, verifies the real Docker config is accessible (not hidden). | | **Simulated exfiltration: base64 encoding** | Runs `cat ... | base64` on hidden credential file. Gets empty output. | | **Multiple encoding attempts** | Runs `cat ... | base64 | xxd -p` pipeline. Still gets empty output. | | **Grep for tokens finds nothing** | Greps for `oauth_token`, `_authToken`, `auth:` patterns. Finds nothing. | @@ -194,7 +192,6 @@ This is a critical security layer. In real agentic workflows: - A prompt injection attack could instruct the agent to read credential files - Credential hiding prevents exfiltration of Docker Hub tokens, GitHub CLI tokens, NPM auth tokens - The MCP logs hiding prevents reading log files that might contain sensitive operation details -- The `--allow-full-filesystem-access` flag is a conscious security trade-off for trusted workloads ### Gaps and Missing Coverage diff --git a/docs/test-analysis/test-infra.md b/docs/test-analysis/test-infra.md index 6631e774c..8ad078ec8 100644 --- a/docs/test-analysis/test-infra.md +++ b/docs/test-analysis/test-infra.md @@ -82,7 +82,6 @@ interface AwfOptions { tty?: boolean; dnsServers?: string[]; allowHostPorts?: string; - allowFullFilesystemAccess?: boolean; enableApiProxy?: boolean; } ``` diff --git a/docs/usage.md b/docs/usage.md index 6e7f2ae71..20c299231 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -43,12 +43,6 @@ Options: like PATH) (default: false) -v, --mount Volume mount (can be specified multiple times). Format: host_path:container_path[:ro|rw] - --allow-full-filesystem-access ⚠️ SECURITY WARNING: Mount entire host filesystem with read-write access. - This DISABLES selective mounting security and exposes ALL files including: - - Docker Hub tokens (~/.docker/config.json) - - GitHub CLI tokens (~/.config/gh/hosts.yml) - - NPM, Cargo, Composer credentials - Only use if you cannot use --mount for specific directories. (default: false) --container-workdir Working directory inside the container (should match GITHUB_WORKSPACE for path consistency) --dns-servers Comma-separated list of trusted DNS servers. DNS traffic is ONLY diff --git a/src/cli.ts b/src/cli.ts index 3805b7964..434249c2f 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -675,16 +675,6 @@ program (value, previous: string[] = []) => [...previous, value], [] ) - .option( - '--allow-full-filesystem-access', - '⚠️ SECURITY WARNING: Mount entire host filesystem with read-write access.\n' + - ' This DISABLES selective mounting security and exposes ALL files including:\n' + - ' - Docker Hub tokens (~/.docker/config.json)\n' + - ' - GitHub CLI tokens (~/.config/gh/hosts.yml)\n' + - ' - NPM, Cargo, Composer credentials\n' + - ' Only use if you cannot use --mount for specific directories.', - false - ) .option( '--container-workdir ', 'Working directory inside the container (should match GITHUB_WORKSPACE for path consistency)' @@ -981,7 +971,6 @@ program additionalEnv: Object.keys(additionalEnv).length > 0 ? additionalEnv : undefined, envAll: options.envAll, volumeMounts, - allowFullFilesystemAccess: options.allowFullFilesystemAccess, containerWorkDir: options.containerWorkdir, dnsServers, proxyLogsDir: options.proxyLogsDir, diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts index fc432532e..0a5c1aae8 100644 --- a/src/docker-manager.test.ts +++ b/src/docker-manager.test.ts @@ -549,35 +549,7 @@ describe('docker-manager', () => { expect(volumes.some((v: string) => v.includes('/dev/null'))).toBe(true); }); - it('should use blanket mount when allowFullFilesystemAccess is true', () => { - const configWithFullAccess = { - ...mockConfig, - allowFullFilesystemAccess: true, - }; - const result = generateDockerCompose(configWithFullAccess, mockNetworkConfig); - const agent = result.services.agent; - const volumes = agent.volumes as string[]; - - // Should include blanket /:/host:rw mount - expect(volumes).toContain('/:/host:rw'); - // Docker socket should still be hidden for security even with full filesystem access - expect(volumes).toContain('/dev/null:/host/var/run/docker.sock:ro'); - // But credential files should NOT be hidden (user opted in to full access) - expect(volumes.some((v: string) => v.includes('/dev/null') && v.includes('.docker/config.json'))).toBe(false); - }); - it('should use blanket mount when allowFullFilesystemAccess is true', () => { - const configWithFullAccess = { - ...mockConfig, - allowFullFilesystemAccess: true, - }; - const result = generateDockerCompose(configWithFullAccess, mockNetworkConfig); - const agent = result.services.agent; - const volumes = agent.volumes as string[]; - - // Should include blanket /:/host:rw mount - expect(volumes).toContain('/:/host:rw'); - }); it('should use selective mounts by default', () => { const result = generateDockerCompose(mockConfig, mockNetworkConfig); diff --git a/src/docker-manager.ts b/src/docker-manager.ts index 2b9236d4f..e92ab8d45 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -722,8 +722,7 @@ export function generateDockerCompose( // 1. Mount ONLY the workspace directory ($GITHUB_WORKSPACE or cwd) // 2. Mount ~/.copilot/logs separately for Copilot CLI logging // 3. Hide credential files by mounting /dev/null over them (defense-in-depth) - // 4. Provide escape hatch (--allow-full-filesystem-access) for edge cases - // 5. Allow users to add specific mounts via --mount flag + // 4. Allow users to add specific mounts via --mount flag // // This ensures that credential files in $HOME are never mounted, making them // inaccessible even if prompt injection succeeds. @@ -746,84 +745,70 @@ export function generateDockerCompose( }); } - // Apply security policy: selective mounting vs full filesystem access - if (config.allowFullFilesystemAccess) { - // User explicitly opted into full filesystem access - log security warning - logger.warn('⚠️ SECURITY WARNING: Full filesystem access enabled'); - logger.warn(' The entire host filesystem is mounted with read-write access'); - logger.warn(' This exposes sensitive credential files to potential prompt injection attacks'); - logger.warn(' Consider using selective mounting (default) or --volume-mount for specific directories'); + // Default: Selective mounting for security against credential exfiltration + // This provides protection against prompt injection attacks + logger.debug('Using selective mounting for security (credential files hidden)'); - // Add blanket mount for full filesystem access - agentVolumes.unshift('/:/host:rw'); - } else { - // Default: Selective mounting for security against credential exfiltration - // This provides protection against prompt injection attacks - logger.debug('Using selective mounting for security (credential files hidden)'); + // SECURITY: Hide credential files by mounting /dev/null over them + // This prevents prompt-injected commands from reading sensitive tokens + // even if the attacker knows the file paths + // + // The home directory is mounted at both $HOME and /host$HOME. + // We must hide credentials at BOTH paths to prevent bypass attacks. + const credentialFiles = [ + `${effectiveHome}/.docker/config.json`, // Docker Hub tokens + `${effectiveHome}/.npmrc`, // NPM registry tokens + `${effectiveHome}/.cargo/credentials`, // Rust crates.io tokens + `${effectiveHome}/.composer/auth.json`, // PHP Composer tokens + `${effectiveHome}/.config/gh/hosts.yml`, // GitHub CLI OAuth tokens + // SSH private keys (CRITICAL - server access, git operations) + `${effectiveHome}/.ssh/id_rsa`, + `${effectiveHome}/.ssh/id_ed25519`, + `${effectiveHome}/.ssh/id_ecdsa`, + `${effectiveHome}/.ssh/id_dsa`, + // Cloud provider credentials (CRITICAL - infrastructure access) + `${effectiveHome}/.aws/credentials`, + `${effectiveHome}/.aws/config`, + `${effectiveHome}/.kube/config`, + `${effectiveHome}/.azure/credentials`, + `${effectiveHome}/.config/gcloud/credentials.db`, + ]; - // SECURITY: Hide credential files by mounting /dev/null over them - // This prevents prompt-injected commands from reading sensitive tokens - // even if the attacker knows the file paths - // - // The home directory is mounted at both $HOME and /host$HOME. - // We must hide credentials at BOTH paths to prevent bypass attacks. - const credentialFiles = [ - `${effectiveHome}/.docker/config.json`, // Docker Hub tokens - `${effectiveHome}/.npmrc`, // NPM registry tokens - `${effectiveHome}/.cargo/credentials`, // Rust crates.io tokens - `${effectiveHome}/.composer/auth.json`, // PHP Composer tokens - `${effectiveHome}/.config/gh/hosts.yml`, // GitHub CLI OAuth tokens - // SSH private keys (CRITICAL - server access, git operations) - `${effectiveHome}/.ssh/id_rsa`, - `${effectiveHome}/.ssh/id_ed25519`, - `${effectiveHome}/.ssh/id_ecdsa`, - `${effectiveHome}/.ssh/id_dsa`, - // Cloud provider credentials (CRITICAL - infrastructure access) - `${effectiveHome}/.aws/credentials`, - `${effectiveHome}/.aws/config`, - `${effectiveHome}/.kube/config`, - `${effectiveHome}/.azure/credentials`, - `${effectiveHome}/.config/gcloud/credentials.db`, - ]; + credentialFiles.forEach(credFile => { + agentVolumes.push(`/dev/null:${credFile}:ro`); + }); - credentialFiles.forEach(credFile => { - agentVolumes.push(`/dev/null:${credFile}:ro`); - }); - - logger.debug(`Hidden ${credentialFiles.length} credential file(s) via /dev/null mounts`); - } + logger.debug(`Hidden ${credentialFiles.length} credential file(s) via /dev/null mounts`); // Also hide credentials at /host paths (chroot mounts home at /host$HOME too) - if (!config.allowFullFilesystemAccess) { - logger.debug('Hiding credential files at /host paths'); - - // Note: In chroot mode, effectiveHome === getRealUserHome() (see line 433), - // so we reuse effectiveHome here instead of calling getRealUserHome() again. - const chrootCredentialFiles = [ - `/dev/null:/host${effectiveHome}/.docker/config.json:ro`, - `/dev/null:/host${effectiveHome}/.npmrc:ro`, - `/dev/null:/host${effectiveHome}/.cargo/credentials:ro`, - `/dev/null:/host${effectiveHome}/.composer/auth.json:ro`, - `/dev/null:/host${effectiveHome}/.config/gh/hosts.yml:ro`, - // SSH private keys (CRITICAL - server access, git operations) - `/dev/null:/host${effectiveHome}/.ssh/id_rsa:ro`, - `/dev/null:/host${effectiveHome}/.ssh/id_ed25519:ro`, - `/dev/null:/host${effectiveHome}/.ssh/id_ecdsa:ro`, - `/dev/null:/host${effectiveHome}/.ssh/id_dsa:ro`, - // Cloud provider credentials (CRITICAL - infrastructure access) - `/dev/null:/host${effectiveHome}/.aws/credentials:ro`, - `/dev/null:/host${effectiveHome}/.aws/config:ro`, - `/dev/null:/host${effectiveHome}/.kube/config:ro`, - `/dev/null:/host${effectiveHome}/.azure/credentials:ro`, - `/dev/null:/host${effectiveHome}/.config/gcloud/credentials.db:ro`, - ]; + logger.debug('Hiding credential files at /host paths'); + + // Note: In chroot mode, effectiveHome === getRealUserHome() (see line 433), + // so we reuse effectiveHome here instead of calling getRealUserHome() again. + const chrootCredentialFiles = [ + `/dev/null:/host${effectiveHome}/.docker/config.json:ro`, + `/dev/null:/host${effectiveHome}/.npmrc:ro`, + `/dev/null:/host${effectiveHome}/.cargo/credentials:ro`, + `/dev/null:/host${effectiveHome}/.composer/auth.json:ro`, + `/dev/null:/host${effectiveHome}/.config/gh/hosts.yml:ro`, + // SSH private keys (CRITICAL - server access, git operations) + `/dev/null:/host${effectiveHome}/.ssh/id_rsa:ro`, + `/dev/null:/host${effectiveHome}/.ssh/id_ed25519:ro`, + `/dev/null:/host${effectiveHome}/.ssh/id_ecdsa:ro`, + `/dev/null:/host${effectiveHome}/.ssh/id_dsa:ro`, + // Cloud provider credentials (CRITICAL - infrastructure access) + `/dev/null:/host${effectiveHome}/.aws/credentials:ro`, + `/dev/null:/host${effectiveHome}/.aws/config:ro`, + `/dev/null:/host${effectiveHome}/.kube/config:ro`, + `/dev/null:/host${effectiveHome}/.azure/credentials:ro`, + `/dev/null:/host${effectiveHome}/.config/gcloud/credentials.db:ro`, + ]; - chrootCredentialFiles.forEach(mount => { - agentVolumes.push(mount); - }); + chrootCredentialFiles.forEach(mount => { + agentVolumes.push(mount); + }); - logger.debug(`Hidden ${chrootCredentialFiles.length} credential file(s) at /host paths`); - } + logger.debug(`Hidden ${chrootCredentialFiles.length} credential file(s) at /host paths`); // Agent service configuration const agentService: any = { diff --git a/src/types.ts b/src/types.ts index 4e95b5178..6cf0d7413 100644 --- a/src/types.ts +++ b/src/types.ts @@ -250,51 +250,11 @@ export interface WrapperConfig { * * When specified, selective mounting is used (only essential directories + custom mounts). * When not specified, selective mounting is still used by default for security. - * Use --allow-full-filesystem-access to opt into blanket mounting. * * @example ['/workspace:/workspace:ro', '/data:/data:rw'] */ volumeMounts?: string[]; - /** - * Allow full filesystem access (blanket /:/host:rw mount) - * - * **SECURITY WARNING**: This flag disables AWF's security protection against - * credential exfiltration via prompt injection attacks. It mounts the entire - * host filesystem with read-write access, exposing ALL files including: - * - Docker Hub tokens (~/.docker/config.json) - * - GitHub CLI tokens (~/.config/gh/hosts.yml) - * - NPM tokens (~/.npmrc) - * - Rust crates.io tokens (~/.cargo/credentials) - * - PHP Composer tokens (~/.composer/auth.json) - * - And any other sensitive files on the host - * - * **Default behavior (false)**: Selective mounting is used, which only mounts: - * - User home directory (for workspace access) - * - In GitHub Actions, the workspace directory ($GITHUB_WORKSPACE) is typically a - * subdirectory of $HOME and is therefore accessible via this home directory mount - * - Essential directories (/tmp, ~/.copilot/logs) - * - Credential files are hidden by mounting /dev/null over them - * - * **Only enable this if**: - * - You need access to files outside the standard directories - * - You cannot use --volume-mount to specify needed directories - * - You understand and accept the security risks - * - * @default false - * @example - * ```bash - * # Avoid this - use selective mounting instead - * awf --allow-full-filesystem-access --allow-domains github.com -- curl https://api.github.com - * - * # Preferred - use selective mounting (default) - * awf --allow-domains github.com -- curl https://api.github.com - * - * # If you need specific directories, mount them explicitly - * awf --volume-mount /data:/data:ro --allow-domains github.com -- curl https://api.github.com - * ``` - */ - allowFullFilesystemAccess?: boolean; /** * Working directory inside the agent execution container diff --git a/tests/fixtures/awf-runner.ts b/tests/fixtures/awf-runner.ts index 8efe42abe..8656e8fcd 100644 --- a/tests/fixtures/awf-runner.ts +++ b/tests/fixtures/awf-runner.ts @@ -17,7 +17,6 @@ export interface AwfOptions { tty?: boolean; // Allocate pseudo-TTY (required for interactive tools like Claude Code) dnsServers?: string[]; // DNS servers to use (e.g., ['8.8.8.8', '2001:4860:4860::8888']) allowHostPorts?: string; // Ports or port ranges to allow for host access (e.g., '3000' or '3000-8000') - allowFullFilesystemAccess?: boolean; // Allow full filesystem access (disables selective mounting security) enableApiProxy?: boolean; // Enable API proxy sidecar for LLM credential management } @@ -100,11 +99,6 @@ export class AwfRunner { args.push('--allow-host-ports', options.allowHostPorts); } - // Add allow-full-filesystem-access flag - if (options.allowFullFilesystemAccess) { - args.push('--allow-full-filesystem-access'); - } - // Add enable-api-proxy flag if (options.enableApiProxy) { args.push('--enable-api-proxy'); @@ -254,11 +248,6 @@ export class AwfRunner { args.push('--allow-host-ports', options.allowHostPorts); } - // Add allow-full-filesystem-access flag - if (options.allowFullFilesystemAccess) { - args.push('--allow-full-filesystem-access'); - } - // Add enable-api-proxy flag if (options.enableApiProxy) { args.push('--enable-api-proxy'); diff --git a/tests/integration/credential-hiding.test.ts b/tests/integration/credential-hiding.test.ts index 1aa3b6b5d..938832d3d 100644 --- a/tests/integration/credential-hiding.test.ts +++ b/tests/integration/credential-hiding.test.ts @@ -227,53 +227,6 @@ describe('Credential Hiding Security', () => { }, 120000); }); - describe('Full Filesystem Access Flag (--allow-full-filesystem-access)', () => { - test('Test 10: Full filesystem access shows security warnings', async () => { - const result = await runner.runWithSudo( - 'echo "test"', - { - allowDomains: ['github.com'], - logLevel: 'debug', - timeout: 60000, - allowFullFilesystemAccess: true, - } - ); - - expect(result).toSucceed(); - - // Check for multiple security warning messages - expect(result.stderr).toMatch(/⚠️.*SECURITY WARNING/i); - expect(result.stderr).toMatch(/entire host filesystem.*mounted|Full filesystem access/i); - }, 120000); - - test('Test 11: With full access, Docker config is NOT hidden', async () => { - const homeDir = os.homedir(); - const dockerConfig = `${homeDir}/.docker/config.json`; - - // First check if file exists on host - const fileExists = fs.existsSync(dockerConfig); - - if (fileExists) { - const result = await runner.runWithSudo( - `wc -c ${dockerConfig} 2>&1 | grep -v "^\\[" | head -1`, - { - allowDomains: ['github.com'], - logLevel: 'debug', - timeout: 60000, - allowFullFilesystemAccess: true, - } - ); - - expect(result).toSucceed(); - // With full access, file size should match real file (not 0 bytes from /dev/null) - const realSize = fs.statSync(dockerConfig).size; - const output = result.stdout.trim(); - if (output && realSize > 0) { - expect(output).toContain(realSize.toString()); - } - } - }, 120000); - }); describe('Security Verification', () => { test('Test 12: Simulated exfiltration attack gets empty data', async () => {