T09 · Insecure Skill Coding Practices
- Location
scripts/timeout-risk-audit.sh:345- Finding
Unescaped Terminal Control Characters Allow CI Log and Terminal Output Spoofing
- Content
View full analysis
Vulnerability Details
File Location:
scripts/timeout-risk-audit.sh, lines 345–350
Vulnerability Type: Untrusted terminal and CI-log output
Risk Level: LowVulnerable Code
python for row in ranked_groups[:top_n]: print( f"- [{row['severity']}] {row['repository']} :: {row['workflow']} :: {row['job_name']} " f"max_duration_s={row['max_duration_seconds']} avg_duration_s={row['avg_duration_seconds']} " f"critical={row['critical_count']} warn={row['warn_count']} timed_out={row['timed_out_count']} " f"instances={row['instances']}" )Technical Analysis
The
repository,workflow, andjob_namevalues originate from imported GitHub Actions run JSON. They are interpolated directly into text output without escaping terminal control characters.JSON decoding preserves encoded control characters such as escape (
\u001b), carriage return (\r), and newline (\n). If an attacker can influence workflow or job metadata, crafted values can inject ANSI escape sequences or additional log lines. A terminal or CI log renderer may interpret these sequences rather than displaying them literally.This issue affects text output. JSON output uses
json.dumps, which escapes control characters and is not affected in the same way.Attack Path
- An attacker who can modify workflow metadata creates a workflow or job name containing encoded terminal control characters.
- GitHub Actions produces a run containing the crafted metadata.
- The run is exported to JSON and included in a path matched by
RUN_GLOB. - The audit script parses the attacker-controlled value and stores it without sanitization.
- The script runs with
OUTPUT_FORMAT=text. - The crafted value is printed directly to a terminal or CI log.
- The renderer interprets the control sequence, potentially hiding findings, changing visible terminal content, or injecting misleading log entries.
Impact Assessment
The issue can compromi ...[truncated 403 chars]
- Remediation
View remediation
Remediation Suggestions
Sanitize every untrusted value before writing text-mode output:
- Replace C0 and C1 control characters with visible escaped representations.
- At minimum, escape escape characters, carriage returns, newlines, backspaces, tabs, and delete characters.
- Apply the sanitizer consistently to repository names, workflow names, job names, branches, URLs, file paths, and parse-error messages.
- Preserve JSON mode, where
json.dumpsalready encodes control characters safely. - Add regression tests using values containing
\u001b[2J,\r, and\n.
For example:
python def sanitize_text(value): text = str(value) return ''.join( ch if ch.isprintable() and ch not in '\r\n' else f'\\x{ord(ch):02x}' for ch in text )Use the sanitized values in all text-report
printcalls:python repository = sanitize_text(row['repository']) workflow = sanitize_text(row['workflow']) job_name = sanitize_text(row['job_name']) print( f"- [{row['severity']}] {repository} :: {workflow} :: {job_name} " f"max_duration_s={row['max_duration_seconds']} " f"avg_duration_s={row['avg_duration_seconds']} " f"critical={row['critical_count']} warn={row['warn_count']} " f"timed_out={row['timed_out_count']} instances={row['instances']}" )
