Shell Provider
This document describes the implementation details of the Shell exec provider for executing commands via /bin/sh.
Provider Selection
The Shell provider is selected when provider: shell is explicitly specified in the resource properties. It has a lower priority (99) than the Posix provider (1), so it is never automatically selected.
Availability: The provider checks for the existence of /bin/sh via util.FileExists(). If /bin/sh does not exist, the provider is not available.
Comparison with Posix Provider
| Feature | Shell | Posix |
|---|---|---|
| Shell invocation | Yes (/bin/sh -c) | No |
Pipes (|) | Supported | Not supported |
Redirections (>, <, >>) | Supported | Not supported |
Shell builtins (cd, export, source) | Supported | Not supported |
Glob expansion (*.txt, ?) | Supported | Not supported |
Command substitution ($(...), `...`) | Supported | Not supported |
Variable expansion ($VAR, ${VAR}) | Supported | Not supported |
Logical operators (&&, ||) | Supported | Not supported |
| Argument parsing | Passed as single string | shellquote.Split() |
| Security | Shell injection possible | Lower attack surface |
When to use Shell:
- Commands with pipes:
cat file.txt | grep pattern | sort - Commands with redirections:
echo "data" > /tmp/file - Commands with shell builtins:
cd /tmp && pwd - Commands with variable expansion:
echo $HOME - Complex one-liners with logical operators
When to use Posix (default):
- Simple commands with arguments
- When shell features are not needed
- For better security (no shell injection risk)
Operations
Execute
Process:
- Determine command source (
Commandproperty orNameifCommandis empty) - Validate command is not empty
- Execute via
CommandRunner.ExecuteWithOptions()with/bin/sh -c "<command>" - Optionally log output line-by-line if
LogOutputis enabled
Execution Method:
The entire command string is passed to the shell as a single argument:
This allows the shell to interpret all shell syntax, including:
- Pipes and redirections
- Variable expansion
- Glob patterns
- Command substitution
- Logical operators
Execution Options:
| Option | Value | Description |
|---|---|---|
Command | /bin/sh | Shell executable path |
Args | ["-c", "<command>"] | Shell flag and command string |
Cwd | properties.Cwd | Working directory |
Environment | properties.Environment | Additional env vars (KEY=VALUE format) |
Path | properties.Path | Search path for executables |
Timeout | properties.ParsedTimeout | Maximum execution time |
Output Logging:
When LogOutput: true is set and a user logger is provided:
Each line of stdout is logged as a separate Info message.
Error Handling:
| Condition | Behavior |
|---|---|
| Empty command string | Return error: “no command to execute” |
| Runner not configured | Return error: “no command runner configured” |
| Shell not found | Provider not available (checked at selection time) |
| Command execution fails | Return error from runner |
| Non-zero exit code | Return exit code (not an error by itself) |
Status
Process:
- Create state with
EnsurePresent(exec resources are always “present”) - Check if
Createsfile exists viautil.FileExists() - Set
CreatesSatisfiedaccordingly
State Fields:
| Field | Value |
|---|---|
Protocol | io.choria.ccm.v1.resource.exec.state |
ResourceType | exec |
Name | Resource name |
Ensure | present (always) |
CreatesSatisfied | true if Creates file exists |
Use Cases
Pipes and Filters
Conditional Execution
Complex Scripts
Variable Expansion
Idempotency
The shell provider uses the same idempotency mechanisms as the posix provider:
Creates File
If creates is specified and the file exists, the command does not run:
RefreshOnly Mode
When refreshonly: true, the command only runs when triggered by a subscribed resource:
Exit Code Validation
The returns property specifies acceptable exit codes (default: [0]):
Security Considerations
Shell Injection Risk
The shell provider passes the command string directly to /bin/sh, making it vulnerable to shell injection if user input is incorporated:
Mitigations:
- Validate and sanitize any templated values
- Use the posix provider when shell features aren’t needed
- Prefer explicit file paths over user-provided values
Environment Variable Exposure
Shell commands can access environment variables, including sensitive ones:
Consider using the environment property to explicitly pass required variables rather than relying on inherited environment.
Command Logging
The full command string (including any expanded variables) may appear in logs. Avoid embedding secrets directly in commands:
Platform Support
The shell provider requires /bin/sh to be available. This is standard on:
- Linux distributions
- macOS
- BSD variants
- Most Unix-like systems
On Windows, the provider will not be available unless /bin/sh exists (e.g., via WSL or Cygwin).
Shell Compatibility
The provider uses /bin/sh, which is typically:
- Linux: Often a symlink to
bash,dash, or another POSIX-compliant shell - macOS:
/bin/shisbash(older) orzsh(newer) in POSIX mode - BSD: Usually
ashor similar
For maximum portability, use POSIX shell syntax and avoid bash-specific features like:
- Arrays (
arr=(1 2 3)) [[conditionals (use[instead)source(use.instead)- Process substitution (
<(command))