Where Should Botie Run Code He Has Just Written?
A test runner executes repository code. Give it a deliberately small environment, then verify the filesystem, network and resource limits that environment really enforces.
16 sections · 6 questions · 1 coding challenge
The arrow keys move between sections too.
A Process Needed Limits on More Than Its Files
BOTIE
Generated code makes the reason for isolation easy to see: we need to observe what a program does before trusting it. The isolation mechanism and its configuration are part of that experiment. A container with a privileged host mount can invalidate the boundary we thought we had created.
Sources for this history
Running the Tests Also Runs the Repository
The patch is ready to test. Botie runs the test command, and a project hook executes before the first assertion. That hook has the same access as the process that launched it. If our cloud credentials are in that environment, the word test does not make them inaccessible.
Let’s give this job only what it needs: its candidate files, a place for temporary output and a bounded amount of computation. We will inspect an execution profile, then discuss what has to be checked in the real runner.
These are invented records and deterministic policy checks. The browser runs no agent, shell command, MCP server or network request. Passing these fixtures establishes only the stated policy behaviour.
Step 1 of 2
Which resource does the test need?
The task is an offline parser test.
| Resource | Proposed access | Reason |
|---|---|---|
| Candidate files | Read-only snapshot | Read the code being assessed |
| Scratch directory | Writable | Temporary test output |
| Production credentials | Absent | No production action belongs to this test |
| Network | Disabled | Dependencies were prepared separately |
Candidate files
- Proposed access
- Read-only snapshot
- Reason
- Read the code being assessed
Scratch directory
- Proposed access
- Writable
- Reason
- Temporary test output
Production credentials
- Proposed access
- Absent
- Reason
- No production action belongs to this test
Network
- Proposed access
- Disabled
- Reason
- Dependencies were prepared separately
Step 2 of 2
Configuration is only the beginning
The runtime must enforce what the profile says.
- 1
Review profile
Find excessive access - 2
Launch isolated worker
Apply limits - 3
Probe boundaries
Attempt forbidden access - 4
Collect bounded results
Dispose of the worker
An execution profile needs enforcement beneath it
Both branches retain their findings. A clean fixture proceeds to a runtime test, not straight to production.
Inspect the execution envelope
Review writable mounts separately from network, credential and privilege settings, then preserve all findings.
BOTIE
Profile findings and route
{
"route": "test isolation",
"issues": []
}Follow the operations
1. Execution profile
{ "mounts": [ { "path": "/scratch", "mode": "rw" } ], "credentials": [], "network": "none", "privileged": false, "timeout": 30 }2. Approved writable paths
- 0/scratch
3. Inspect writable mounts
[]
4. Inspect execution privileges
[]
5. Collect configuration gaps
[]
6. Keep the profile findings
{ "route": "test isolation", "issues": [] }7. Result
{ "route": "test isolation", "issues": [] }
These are invented records and deterministic policy checks. The browser runs no agent, shell command, MCP server or network request. Passing these fixtures establishes only the stated policy behaviour.
1. Decide what the program must be able to touch
List required input files, output locations and services. For the parser, a source snapshot and scratch space are enough. Keep the job away from the developer’s home directory, credential files and production sockets.
Treat installation and build scripts as executable code too. An apparently harmless test command can import application modules, run hooks or start child processes. Restrict the whole execution tree, not only the first command string.
The task’s purpose does not limit what the process can do. Its actual permissions do.
2. Choose an isolation boundary suited to the threat
A hardened container may be suitable for a constrained internal task. Untrusted multi-tenant code can require stronger isolation, such as a separately managed virtual machine or dedicated execution service. Account for shared-kernel risk, runtime vulnerabilities and patching.
Avoid privileged mode, unnecessary capabilities and access to the container engine socket. Run without administrative host authority. A container label is a description of packaging, not a guarantee about the configured boundary.
Review the runtime and host configuration together.
3. Limit reads as well as writes
Read-only access can still disclose secrets. Mount only the files the task needs, and keep writable output separate from trusted configuration. Resolve filesystem targets using the platform’s safe primitives; include symlinks and races in the design.
The workshop’s mount paths are trusted fixture labels. Its comparison cannot discover an actual bind mount, resolve a link or prevent a process from opening a file. Those properties require checks against the launched environment.
Test that a forbidden file cannot be read, not merely that it cannot be overwritten.
4. Remove ambient credentials and network routes
Build a minimal environment instead of inheriting the developer’s shell environment. Where a job needs a credential, use a short-lived task-scoped identity supplied by a broker. Avoid placing the credential in model context or exposing a general credential store.
Default-deny network egress is useful for offline jobs. If access is needed, constrain destinations and protocols at the network layer. Include redirects, DNS changes, private addresses and cloud metadata endpoints in the threat model.
An application URL check and an enforced egress policy address different parts of the route.
Check yourself
Questions on Parts 1 to 4
Question 1 of 2
Read-only access prevents credential disclosure.
Consider this example.
A parser test cannot write the home directory but can read a cloud credential file.5. Keep the command interface narrow
Prefer a fixed executable and validated arguments for a specific operation over arbitrary shell text. Avoid shell interpolation of model-written strings. In Python, argument-list invocation with shell=False avoids one class of shell parsing, but the chosen program can still interpret dangerous options or execute code.
Some operating-system launch paths have additional behaviour, including Windows batch-file handling. Use the documented subprocess rules for the actual platform. The sandbox must remain effective even when an allowed program runs hostile repository code.
A safe command representation does not establish that the command’s behaviour is safe.
Review the intended arguments as data
This program builds a fixed command description without executing anything.
job = {"executable":"python", "arguments":["-m", "pytest", "tests/test_date.py"]}
print(job["arguments"][0])-mAn argument list is inspectable. Executing it still belongs in the restricted worker.
6. Bound the work and the output
Set time, memory, CPU, process-count and output-size limits appropriate to the job. A timeout should terminate the managed process group or execution environment; killing only a parent process may leave children running.
Limit concurrent jobs as well as each job. A thousand individually bounded tasks can still exhaust a service. Keep cancellation and cleanup observable, and avoid releasing a reservation while the underlying work is still active.
A resource limit is useful only if something enforces it when the program ignores your request to stop.
7. Prepare dependencies through a separate route
An offline execution profile works better when dependencies are already prepared in a reviewed image or artifact. Use a controlled fetch stage with its own credentials and cache policy. Preserve the exact inputs supplied to the test runner.
A lockfile constrains versions but does not prove the packages are trustworthy. Installation hooks and transitive dependencies still need review according to risk. We will connect that evidence to the released artifact in the next lesson.
Separate fetching from executing so network access is not automatically inherited by every test.
8. Probe the launched environment
Run harmless isolation checks against disposable fixtures: try reading an excluded file, writing outside scratch, reaching a prohibited endpoint and exceeding a small resource quota. Observe denial from the actual runtime.
Repeat after changing images, mounts, runner versions or orchestration policy. Keep these checks outside the candidate’s control. A profile that passes our browser audit is only eligible for this next test.
The configuration and the observed enforcement should agree.
9. Bring back only the result you intended to collect
Collect bounded logs and explicitly selected artifacts. Treat those artifacts as untrusted until checked. A generated HTML report can contain active content; a filename can attempt to escape an extraction directory.
Destroy the disposable worker after collecting results, expire task credentials, and handle failures during cleanup. Keep enough protected metadata to diagnose what ran without retaining unnecessary private input.
The output channel is another boundary, even after the process has stopped.
Check yourself
Questions on Parts 1 to 9
Question 1 of 4
What must the reviewer investigate?
Use the information below.
A container mounts the host container-engine socket.Try it with Botie
Build with Botie
Write and run Python