permission-denials.integration.test.ts93 lines · main
1// Integration test for CLAUDE.md §7.3 — customer code that tries to step
2// outside the per-isolate sandbox (read /etc, run subprocesses, peek at
3// host env) must be denied at the Deno permission layer. The runtime
4// surfaces the denial back as a normal value here (the customer code
5// catches the error) so we're testing that the denial mechanism fires —
6// not how the runtime maps it to a RuntimeErrorCode.
7
8import { describe, expect, test } from 'bun:test';
9
10import { runIntegrationFixture } from './test-helpers.js';
11
12describe('permission denials (integration)', () => {
13 test('reading outside /tmp/<id> denied', async () => {
14 const { result, cleanup } = await runIntegrationFixture({
15 fnName: 'test',
16 deploymentId: 'd1',
17 fnSource: `
18 import { query } from '@briven/cli/server';
19 export const test = query(async () => {
20 try {
21 await Deno.readTextFile('/etc/passwd');
22 return 'ok';
23 } catch (e) {
24 return (e as Error).name;
25 }
26 });
27 `,
28 });
29 try {
30 expect(result.ok).toBe(true);
31 if (result.ok) {
32 // Deno 2.x may use NotCapable, PermissionDenied, or similar; accept the family.
33 expect(['PermissionDenied', 'NotCapable']).toContain(result.value);
34 }
35 } finally {
36 await cleanup();
37 }
38 }, 30_000);
39
40 test('Deno.Command (subprocess) denied', async () => {
41 const { result, cleanup } = await runIntegrationFixture({
42 fnName: 'test',
43 deploymentId: 'd1',
44 fnSource: `
45 import { query } from '@briven/cli/server';
46 export const test = query(async () => {
47 try {
48 const cmd = new Deno.Command('ls');
49 await cmd.output();
50 return 'ok';
51 } catch (e) {
52 return (e as Error).name;
53 }
54 });
55 `,
56 });
57 try {
58 expect(result.ok).toBe(true);
59 if (result.ok) {
60 expect(['PermissionDenied', 'NotCapable']).toContain(result.value);
61 }
62 } finally {
63 await cleanup();
64 }
65 }, 30_000);
66
67 test('env access for unallowed key returns undefined or denies', async () => {
68 const { result, cleanup } = await runIntegrationFixture({
69 fnName: 'test',
70 deploymentId: 'd1',
71 fnSource: `
72 import { query } from '@briven/cli/server';
73 export const test = query(async () => {
74 try {
75 return Deno.env.get('NOT_ALLOWED_KEY') ?? 'undefined';
76 } catch (e) {
77 return (e as Error).name;
78 }
79 });
80 `,
81 });
82 try {
83 expect(result.ok).toBe(true);
84 if (result.ok) {
85 // Either Deno returns undefined when --allow-env list is empty (no allowlist
86 // → no access), or it denies. Both prove the customer can't reach env.
87 expect(['undefined', 'PermissionDenied', 'NotCapable']).toContain(result.value);
88 }
89 } finally {
90 await cleanup();
91 }
92 }, 30_000);
93});