T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:434
- Finding
- Arbitrary Code Execution Through Unsafe Terraform Filename Interpolation## Vulnerability Details **File Location**: `SKILL.md`, lines 434-463 **Vulnerability Type**: Command and Python code injection through an untrusted filename **Risk Level**: High ### Vulnerable Code ```bash # Find Terraform resource blocks missing tags rg -l 'resource "aws_' . -g '*.tf' | while read tf_file; do python3 -c " import re with open('$tf_file') as f: content = f.read() blocks = re.finditer(r'resource\s+\"(aws_\w+)\"\s+\"(\w+)\"\s*\{', content) for match in blocks: rtype, rname = match.group(1), match.group(2) # Find the block end by counting braces start = match.end() depth = 1 pos = start while depth > 0 and pos < len(content): if content[pos] == '{': depth += 1 elif content[pos] == '}': depth -= 1 pos += 1 block = content[start:pos] if 'tags' not in block: print(f'MISSING tags in $tf_file: {rtype}.{rname}') " done ``` ### Technical Analysis The filename returned by `rg` is assigned to the shell variable `tf_file` and then interpolated directly into the source code passed to `python3 -c`. The interpolation occurs inside a shell double-quoted argument and inside a Python single-quoted string: ```python with open('$tf_file') as f: ``` A repository controls its filenames. A crafted Terraform filename containing quote characters, line breaks, shell substitutions, or Python syntax can therefore terminate or modify the intended Python string. This allows the filename to change the Python program executed during the scan. The loop also uses `while read tf_file` without null-delimited paths or `IFS= read -r`, making handling of backslashes, whitespace, and newline characters unsafe. ### Attack Path 1. An attacker prepares a repository containing an `.tf` file whose contents match `resource "aws_`, ensuring that `rg` returns its filename. 2. The attacker gives the file a specially crafted name containing ...[truncated 1171 chars]
- Remediation
- ## Remediation Suggestions Never interpolate a repository-controlled path into shell or Python source code. Pass the filename as a positional argument and use null-delimited path handling: ```bash rg -l -0 'resource "aws_' . -g '*.tf' | while IFS= read -r -d '' tf_file; do python3 - "$tf_file" <<'PY' import re import sys file_path = sys.argv[1] with open(file_path, encoding="utf-8") as f: content = f.read() blocks = re.finditer( r'resource\s+"(aws_\w+)"\s+"(\w+)"\s*\{', content, ) for match in blocks: rtype, rname = match.group(1), match.group(2) start = match.end() depth = 1 pos = start while depth > 0 and pos < len(content): if content[pos] == "{": depth += 1 elif content[pos] == "}": depth -= 1 pos += 1 block = content[start:pos] if "tags" not in block: print(f"MISSING tags in {file_path}: {rtype}.{rname}") PY done ``` Additional hardening should include running repository scans in a sandbox without production credentials, testing the scanner against filenames containing quotes and newlines, and avoiding source-code generation from untrusted values.
