Using REXX CALL to Run External Commands: Minimal Design, Security, and Failure Handling
When automating system tasks with REXX, the CALL statement can run any OS command. This article outlines a minimal, secure design: whitelist enforcement, output capture, status checks, and cleanup. It also covers failure modes and when to adjust the design for high‑throughput or cross‑platform use.
26 Oct 2025, 10:39 UTC

Problem & Core Takeaway
When automating system tasks from REXX you often need to run external OS commands. The CALL statement offers a simple synchronous interface, but it also introduces security and reliability concerns. The key is to wrap CALL in a minimal design that enforces a command whitelist, captures output safely, checks exit status, and cleans up resources.
Requirements
- REXX interpreter that supports
CALL(IBM REXX on IBM i, IBM REXX on Unix, or REXX on Windows). - Operating‑system permissions to execute the target commands.
- Access to a temporary file system for output capture.
- Optional: REXX thread library if parallel execution is needed.
Minimal Design
- Define a whitelist. Store allowed commands and optional argument patterns in a data structure.
- Validate user input. Before invoking
CALL, ensure the command and arguments match the whitelist. - Invoke
CALLwith OUTPUT capture. If the platform supports theOUTPUTparameter, use it to capture stdout directly; otherwise redirect to a temporary file. - Check
STATUSor return value. Immediately after the call, examineSTATUS(or the CALL return value) to detect errors. - Read and process output. If a temp file was used, read it into a string or array and delete the file.
- Handle exceptions. Wrap the logic in a
TRY/CATCHblock to log failures and maintain a clean state.
Trust & Data Boundaries
Because CALL can execute any command, the trust boundary is the REXX process itself. All user‑supplied data that flows into CALL must be considered untrusted. The whitelist is the primary guard: it limits the command space and prevents injection of arbitrary shell syntax. For example:
/* REXX */
allowedCommands = "ls pwd echo"
/* Validate */
if pos(command, allowedCommands) = 0 then do
/* Reject */
say "Command not allowed: " command
exit 1
end
When arguments are involved, use a regular expression or explicit comparison rather than concatenating raw user input into the command string.
Operational Checks
- Exit status. After
CALL,STATUSholds the exit code. A non‑zero value indicates failure. - Output file existence. If using a temp file, confirm the file was created and is readable.
- File size limits. For commands that can produce large output, check the file size and decide whether to stream or truncate.
- Permissions. Verify that the REXX process has the necessary file system permissions to write the temp file and execute the command.
Failure Modes & Mitigation
| Failure | Cause | Mitigation |
|---|---|---|
| Command not found | Typo or missing path | Check STATUS and log the error; use absolute paths or a wrapper script. |
| Permission denied | Insufficient OS rights | Run REXX under a user with the needed privileges; use SYSTEM on IBM i for controlled privilege escalation. |
| Large output buffer overflow | Command emits > 64K of text | Redirect to a file; avoid capturing in a string. |
| Shell injection | Unvalidated user input | Whitelist and escape; avoid concatenating raw arguments. |
| Resource leak | Temp file not deleted | Use TRY/FINALLY to guarantee cleanup. |
When to Change the Design
- High‑throughput automation. If you need to run thousands of commands per minute, consider spawning background processes or using the REXX thread library to avoid blocking the main interpreter.
- Cross‑platform scripts. When the same REXX code must run on Windows, Unix, and IBM i, abstract the command invocation behind a function that maps logical command names to platform‑specific shell syntax.
- Need for asynchronous output. If you must process output as it streams (e.g., tailing logs), replace the synchronous
CALLwith a background thread that reads from a pipe. - Privileged operations. If commands require elevated rights, use IBM i’s
SYSTEMcommand with thePRIVILEGEoption instead ofCALLto enforce security policies. - Complex argument handling. For commands that need dynamic quoting or shell expansion, construct the command string carefully or delegate to a wrapper script that receives arguments safely.
Concrete Example
Below is a portable REXX snippet that demonstrates the minimal design on Unix and Windows. It runs echo, captures output, checks status, and cleans up.
/* REXX */
/* Parameters */
command = 'echo'
args = 'Hello, World!'
/* Whitelist */
allowed = 'echo'
if command ~= allowed then do
say 'Command not allowed: ' command
exit 1
end
/* Temp file */
file = 'tmp_output_' || time() || '.txt'
/* Build invocation */
cmdLine = command || ' ' || args
/* Execute */
/* On platforms that support OUTPUT, use it; otherwise redirect */
if syscmd('which sh') then do
/* Unix */
call sh -c cmdLine > file
status = STATUS
else
/* Windows */
call cmd /c cmdLine > file
status = STATUS
end
/* Check status */
if status ~= 0 then do
say 'Command failed with exit code' status
/* Read error output if available */
if fileexists(file) then readfile(file, output)
else output = 'No output captured'
say output
exit status
end
/* Read output */
readfile(file, output)
/* Clean up */
if fileexists(file) then delete file
/* Helper functions */
readfile: procedure
parse arg fname, buffer
if fileexists(fname) then do
buffer = ''
do while linesin(fname) > 0
buffer = buffer || linein(fname)
end
end
return buffer
end
fileexists: function
parse arg fname
return fileexists(fname)
end
Note: Replace fileexists and linesin with the appropriate REXX system calls on your platform.
Verification Checklist
- Run the script on a test machine; confirm that the output file contains "Hello, World!".
- Change
commandto a non‑existent name; verify thatSTATUSis non‑zero and the error path is executed. - Execute the script on both Windows and Unix to confirm that the correct shell is invoked and path resolution works.
- Intentionally introduce a large output command (e.g.,
generate 1M lines) and observe that the file size check prevents a buffer overflow.
Limitations
- Platform‑specific behavior: Windows uses
cmd.exe, Unix uses/bin/sh. Path resolution and quoting rules differ. - REXX line buffer limits may truncate output if not redirected to a file.
- Security relies on the whitelist; an attacker who can modify the whitelist script can bypass controls.
- Asynchronous or parallel execution requires additional complexity (threads, pipes).
Conclusion
By encapsulating CALL inside a whitelist‑protected function, capturing output safely, and checking STATUS, you can reliably use REXX to automate system tasks while mitigating common failure modes and security risks. Adapt the design to your platform and workload needs, and always verify the approach in a controlled test environment before deploying to production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.