The direct answer

Prefer narrowly scoped file or task APIs over a general shell tool. When process execution is necessary, keep the executable and options trusted, validate each argument for that program, and constrain the process identity, filesystem access, network access, and resource use outside the model.

Name the operation the workflow actually needs

An agent asked to turn a CSV into a summary does not automatically need a terminal. Give it an operation such as summarize_imported_csv with a validated object identifier and bounded output size. A file service can retrieve the permitted object and a worker can produce the summary. This is easier to reason about than a tool accepting arbitrary command text.

OWASP recommends avoiding direct operating-system commands when a library can perform the task. Its guidance also distinguishes shell metacharacters from argument injection. Removing a semicolon does not make an arbitrary program argument safe.

Keep commands separate from data

If a workflow must call a converter, choose the executable and required options in application code. Pass validated operands through a process API as individual arguments, with shell interpretation disabled where supported. Do not let the agent choose a shell, build a command string, or supply extra flags.

For a synthetic image task, accept an internal image ID and a format chosen from a small supported set. Resolve the ID to a permitted input in the trusted service. Do not accept a model-generated string containing a command, output location, and options together.

Argument arrays prevent a class of shell parsing problems; they do not establish that an operand is safe for the invoked application. A filename beginning with an option prefix may change program behavior. Use the program's documented option terminator where appropriate and still validate the allowed operand. The Python subprocess documentation describes argument sequences and shell behavior; check the equivalent documentation for your runtime and operating system. On Windows, batch files can involve a system shell even when an application does not request one, so do not treat shell=False as a universal guarantee. Prefer a known native executable and review platform-specific behavior.

Enforce access to objects, not convenient path strings

A string starting with an allowed directory name is not a sufficient file authorization decision. Parent-directory traversal, symbolic links, filesystem mounts, alternate path syntax, and replacement between checking and opening can change which object is accessed.

For a small service, the simplest design may be a private working directory created for one job, populated only with that job's approved inputs. Expose opaque object IDs to the agent. Run the worker under an identity that cannot read unrelated application files or credentials. Put outputs in a separately controlled location.

When the application must operate on a shared filesystem, use platform-appropriate facilities that keep resolution anchored to the intended directory and address symlinks and race conditions. The Python os documentation describes directory-descriptor and follow_symlinks support, including platform differences. Merely calling a path-normalization function does not provide the same guarantee.

Give the worker a small environment

Process arguments are only part of the execution context. A converter can read environment variables, discover configuration files, load plugins, access the network, or create additional processes. Review the actual program and its dependencies rather than assuming a familiar command is inert.

Set a deliberate working directory and environment. Avoid passing application credentials into a job that does not require them. Use a dedicated low-privilege identity, storage limits, a time limit, and an output limit. Restrict outbound network access when the task can run without it. Decide how child processes are stopped when a job is cancelled or exceeds a limit.

A container can be part of this design, but its effectiveness depends on its configuration, host boundary, mounts, capabilities, and network policy. A directory named sandbox does not establish isolation.

Verify denials without touching real data

Build a temporary fixture containing harmless files and a marker representing a secret. The marker is synthetic; it must not be a production credential. Exercise the trusted file or process service directly so a model's refusal cannot hide a permissive backend.

  1. Read the permitted fixture through its approved object ID.
  2. Request an object belonging to another synthetic tenant; expect denial.
  3. Submit a path traversal and an unexpected option; expect rejection before process start.
  4. Place a symbolic link to the synthetic marker outside the permitted input set; expect no marker disclosure.
  5. Run a controlled long task and excessive-output task; verify the configured termination and output limits.
  6. Check that cancelled or failed jobs do not leave reusable credentials or public output files.

Record what each check established on the tested operating system. A lexical validator passing unit tests does not demonstrate symlink or concurrent replacement safety. Those properties need tests at the actual file-access boundary.

Keep the limits visible to operators

Document permitted operations, denied inputs, resource ceilings, and what happens when execution status is uncertain. Give operators a way to stop new jobs and investigate a job identifier without dumping input files or secrets into logs. Revisit the policy when the converter, runtime, or deployment environment changes.

This article is a design checklist, not a ready-to-run sandbox implementation or proof that a particular program is secure. Boundary is a prelaunch model router with planned checks on defined request-path patterns, provider policies, and API usage limits. File access, shell execution, tool approvals, and alternate execution paths require their own enforcement.

Primary sources

Official references supporting the guidance. Examples in this guide are synthetic and are not reports of a production incident.

  1. OWASP OS Command Injection Defense Cheat Sheet
  2. Python subprocess documentation
  3. Python os documentation