test: Add E2E for linux sandbox

This commit is contained in:
abhisek
2026-01-14 20:55:50 +05:30
parent a42a3d9528
commit 0eec922a15
2 changed files with 108 additions and 42 deletions
+49
View File
@@ -443,3 +443,52 @@ jobs:
- name: Run Sandbox E2E Test
run: pmg --sandbox npm exec -- node test/sandbox-e2e.js
sandbox-e2e-linux:
name: Sandbox E2E - Linux (Bubblewrap)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Source
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Go
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5
with:
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
check-latest: true
- name: Install Bubblewrap
run: sudo apt-get update && sudo apt-get install -y bubblewrap
- name: Verify Bubblewrap Installation
run: bwrap --version
- name: Build PMG
run: make
- name: Add pmg to PATH
run: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
- name: Setup PMG
run: pmg setup install
- name: Create Test Directories for Sandbox Permissions Tests
run: mkdir -p ~/.aws ~/.gcloud ~/.kube ~/.ssh ~/.gnupg ~/.docker
- name: Create Test Files for Sandbox Permissions Tests
run: |
touch ~/.aws/credentials
touch ~/.gcloud/credentials.json
touch ~/.kube/config
touch ~/.ssh/id_rsa
touch ~/.gnupg/pubring.kbx
touch ~/.docker/config.json
- name: Run Sandbox E2E Test
run: pmg --sandbox --sandbox-profile npm-restrictive npm exec -- node test/sandbox-e2e.js
+59 -42
View File
@@ -6,6 +6,27 @@ const os = require('os');
const home = os.homedir();
const results = { passed: 0, failed: 0, tests: [] };
// Helper to test if a directory is blocked (either EPERM or empty via tmpfs)
function isDirectoryBlocked(dirPath) {
try {
const contents = fs.readdirSync(dirPath);
// On Linux/bwrap, tmpfs makes directory empty (credentials hidden)
// On macOS/seatbelt, access is denied (EPERM)
if (contents.length === 0) {
return { blocked: true, reason: 'empty via tmpfs' };
}
return { blocked: false, reason: 'contents readable' };
} catch (e) {
if (e.code === 'EPERM') {
return { blocked: true, reason: 'EPERM' };
}
if (e.code === 'ENOENT') {
return { blocked: true, reason: 'does not exist', skip: true };
}
return { blocked: true, reason: e.code, skip: true };
}
}
function test(name, fn) {
try {
const result = fn();
@@ -26,66 +47,62 @@ console.log('--- Tests that SHOULD be BLOCKED ---\n');
// Test 1: Read ~/.ssh (should be blocked)
test('BLOCK: Read ~/.ssh directory', () => {
try {
fs.readdirSync(path.join(home, '.ssh'));
console.log(' ❌ FAIL: Could read ~/.ssh');
return false;
} catch (e) {
if (e.code === 'EPERM') {
console.log(' ✅ PASS: ~/.ssh blocked (EPERM)');
return true;
}
console.log(` ⚠️ SKIP: ~/.ssh - ${e.code} (may not exist)`);
return true; // ENOENT is okay if dir doesn't exist
const result = isDirectoryBlocked(path.join(home, '.ssh'));
if (result.skip) {
console.log(` ⚠️ SKIP: ~/.ssh - ${result.reason}`);
return true;
}
if (result.blocked) {
console.log(` ✅ PASS: ~/.ssh blocked (${result.reason})`);
return true;
}
console.log(' ❌ FAIL: Could read ~/.ssh contents');
return false;
});
// Test 2: Read ~/.aws (should be blocked)
test('BLOCK: Read ~/.aws directory', () => {
try {
fs.readdirSync(path.join(home, '.aws'));
console.log(' ❌ FAIL: Could read ~/.aws');
return false;
} catch (e) {
if (e.code === 'EPERM') {
console.log(' ✅ PASS: ~/.aws blocked (EPERM)');
return true;
}
console.log(` ⚠️ SKIP: ~/.aws - ${e.code} (may not exist)`);
const result = isDirectoryBlocked(path.join(home, '.aws'));
if (result.skip) {
console.log(` ⚠️ SKIP: ~/.aws - ${result.reason}`);
return true;
}
if (result.blocked) {
console.log(` ✅ PASS: ~/.aws blocked (${result.reason})`);
return true;
}
console.log(' ❌ FAIL: Could read ~/.aws contents');
return false;
});
// Test 3: Read ~/.kube (should be blocked)
test('BLOCK: Read ~/.kube/config', () => {
try {
fs.readFileSync(path.join(home, '.kube', 'config'));
console.log(' ❌ FAIL: Could read ~/.kube/config');
return false;
} catch (e) {
if (e.code === 'EPERM') {
console.log(' ✅ PASS: ~/.kube/config blocked (EPERM)');
return true;
}
console.log(` ⚠️ SKIP: ~/.kube/config - ${e.code} (may not exist)`);
test('BLOCK: Read ~/.kube directory', () => {
const result = isDirectoryBlocked(path.join(home, '.kube'));
if (result.skip) {
console.log(` ⚠️ SKIP: ~/.kube - ${result.reason}`);
return true;
}
if (result.blocked) {
console.log(` ✅ PASS: ~/.kube blocked (${result.reason})`);
return true;
}
console.log(' ❌ FAIL: Could read ~/.kube contents');
return false;
});
// Test 4: Read ~/.gcloud (should be blocked)
test('BLOCK: Read ~/.gcloud directory', () => {
try {
fs.readdirSync(path.join(home, '.gcloud'));
console.log(' ❌ FAIL: Could read ~/.gcloud');
return false;
} catch (e) {
if (e.code === 'EPERM') {
console.log(' ✅ PASS: ~/.gcloud blocked (EPERM)');
return true;
}
console.log(` ⚠️ SKIP: ~/.gcloud - ${e.code} (may not exist)`);
const result = isDirectoryBlocked(path.join(home, '.gcloud'));
if (result.skip) {
console.log(` ⚠️ SKIP: ~/.gcloud - ${result.reason}`);
return true;
}
if (result.blocked) {
console.log(` ✅ PASS: ~/.gcloud blocked (${result.reason})`);
return true;
}
console.log(' ❌ FAIL: Could read ~/.gcloud contents');
return false;
});
// Test 5: Write to /etc (should be blocked)