T09 · Insecure Skill Coding Practices
- Location
scripts/cooldown.sh:10- Finding
Arbitrary Shell Command Injection Through eval
- Content
View full analysis
Vulnerability Details
File Location:
scripts/cooldown.sh, lines 10-40
Vulnerability Type: Shell command injection
Risk Level: HighVulnerable Code
bash shift 2 COMMAND="$@" if [ -z "$TASK_NAME" ] || [ -z "$COOLDOWN_SECONDS" ]; then echo "Usage: $0 <TASK_NAME> <COOLDOWN_SECONDS> <COMMAND...>" exit 1 fi TIMESTAMP_DIR="./agent_task_manager_data" TIMESTAMP_FILE="$TIMESTAMP_DIR/${TASK_NAME}_last_run.txt" mkdir -p "$TIMESTAMP_DIR" CURRENT_TIME=$(date +%s) LAST_RUN_TIME=0 if [ -f "$TIMESTAMP_FILE" ]; then LAST_RUN_TIME=$(cat "$TIMESTAMP_FILE") fi ELAPSED_TIME=$((CURRENT_TIME - LAST_RUN_TIME)) WAIT_TIME=$((COOLDOWN_SECONDS - ELAPSED_TIME)) if [ "$WAIT_TIME" -gt 0 ]; then echo "⚠️ Cooldown active for $TASK_NAME. Waiting $WAIT_TIME seconds..." sleep "$WAIT_TIME" fi echo "🚀 Executing command for $TASK_NAME..." if eval "$COMMAND"; thenTechnical Analysis
The script collapses all command arguments into one string through
COMMAND="$@"and subsequently passes that string toeval. Theevalbuilt-in instructs the shell to parse the resulting text as shell syntax for a second time.Consequently, shell metacharacters, command substitutions, redirections, pipelines, and separators contained in an argument are interpreted as executable syntax rather than preserved as literal argument data. Quoting arguments when invoking the wrapper does not provide reliable protection because
evalintroduces another parsing stage after the original shell has processed the command line.This is especially dangerous if any higher-level agent, workflow definition, API parameter, or user-controlled value is incorporated into the command passed to the cooldown wrapper.
Attack Path
- An attacker obtains control over all or part of an argument passed to
cooldown.sh. - The calling shell initially passes the malicious value as an argumen ...[truncated 1145 chars]
- An attacker obtains control over all or part of an argument passed to
- Remediation
View remediation
Remediation Suggestions
Preserve command arguments as an array and execute them directly without
eval:bash TASK_NAME="$1" COOLDOWN_SECONDS="$2" shift 2 if [ "$#" -eq 0 ]; then echo "A command is required." >&2 exit 1 fi if "$@"; then echo "$CURRENT_TIME" > "$TIMESTAMP_FILE" echo "✅ Success. Timestamp updated." else echo "❌ Command failed. Timestamp NOT updated." exit 1 fiAdditional hardening should include:
- Reject an invocation that does not contain a command.
- Use
set -euo pipefailwhere compatible with the intended behavior. - Use an allowlist if only specific programs are expected to be executed.
- Pass untrusted values exclusively as arguments, never as command fragments.
- Add tests containing spaces, quotes, semicolons, substitutions, redirections, and newlines to verify that argument data is never reinterpreted as shell syntax.
