T09 · Insecure Skill Coding Practices
Warning
- Location
scripts/failure-owner-audit.sh:487- Finding
Unsanitized GitHub Actions Metadata Enables Terminal and CI Log Injection
- Content
View full analysis
' def normalize_branch(run): return str(run.get('head_branch') or run.get('headBranch') or '').strip() or '' def normalize_actor(run): actor = '' actor_obj = run.get('actor') if isinstance(actor_obj, dict): actor = actor_obj.get('login') or actor_obj.get('name') or actor elif isinstance(actor_obj, str) and actor_obj.strip(): actor = actor_obj.strip() return str(actor).strip() or '' def normalize_conclusion(run): return str(run.get('conclusion') or '').strip().lower() or 'unknown' ``` These values, together with the repository and owner values, are subsequently interpolated directly into terminal output: ```python for row in rows[:top_n]: workflow_label = row['workflow'] if row['workflow'] else '*' print( f"- [{row['severity']}] {row['repository']} :: owner={row.get('owner') or row['actor']} actor={row['actor']} workflow={workflow_label} " f"runs={row['run_count']} total_min={row['total_failure_minutes']} max_min={row['max_failure_minutes']} " f"branches={row['branch_count']} conclusions={row['conclusions']}" ) ``` ### Technical Analysis The script treats GitHub Actions export files and the optional owner-map file as input but does not neutralize terminal control characters in textual fields before generating its default text report. Calling `str(...).strip()` only removes leading and trailing whitespace; it doe ...[truncated 2304 chars]- Remediation
View remediation
= 0x20 and ord(ch) != 0x7f else f'\\x{ord(ch):02x}' for ch in text ) ``` 2. Explicitly escape or remove: - Newline and carriage-return characters. - The escape character (`\x1b`). - Other C0 and C1 terminal control characters. - DEL (`\x7f`). 3. Apply sanitization at the output boundary to repository, owner, actor, workflow, conclusion, parse-error, and path values. Sanitizing at the sink reduces the chance that a newly added input field will bypass normalization. 4. Consider rendering dynamic values with `json.dumps(value, ensure_ascii=True)` in text mode. This visibly quotes newlines and terminal escape characters while preserving the original data for analysis. 5. Add regression tests using values containing `\n`, `\r`, `\x1b[2J`, cursor-control sequences, and other non-printable characters. Assert that emitted text contains escaped representations and never contains raw terminal-control bytes. 6. Continue using `json.dumps()` for JSON mode and ensure downstream consumers treat generated fields as data rather than executable markup. ]]>
