T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/check_ssl.py:187
- Finding
- JSON Output Mode Bypasses Certificate Failure Exit Codes<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_ssl.py`, lines 187–190 **Vulnerability Type**: Inconsistent security failure signaling **Risk Level**: Medium ### Vulnerable Code ```python if args.json: print(json.dumps(results, indent=2)) else: print_report(results, args.warn_days) ``` ### Technical Analysis The documented exit-code policy is implemented inside `print_report()`: warnings produce exit code 1, while expired certificates and connection or verification failures produce exit code 2. However, `main()` calls `print_report()` only when human-readable output is selected. When `--json` is used, the program serializes the results and reaches the end of execution without setting a nonzero exit code. Python consequently exits with status 0 even when results contain statuses such as `expired`, `verification_failed`, `ssl_error`, `timeout`, or `connection_error`. This creates a fail-open condition for CI/CD pipelines, cron jobs, and monitoring systems that use the documented process exit codes rather than parsing JSON content. It also contradicts the exit-code behavior documented in `SKILL.md`. ### Attack Path 1. A deployment or monitoring pipeline invokes the checker with `--json` and relies on its process exit code. 2. A monitored endpoint presents an expired or unverifiable certificate, becomes unreachable, or is manipulated so that TLS verification fails. 3. The checker records the failure in its JSON output. 4. Because JSON mode does not invoke `print_report()`, no nonzero exit status is generated. 5. The process exits with status 0. 6. The calling automation interprets the check as successful and may continue deployment or suppress an operational alert. ### Impact Assessment This issue does not directly grant operating-system privileges or enable arbitrary code execution. Its impact is limited to the integrity of certificate-monitoring and deployment decisions made using the tool. An attacker capable of disrupting ...[truncated 341 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Separate exit-status calculation from presentation so that JSON and human-readable modes enforce identical status semantics. For example: ```python def determine_exit_code(results: list[dict], warn_days: int) -> int: has_failure = any(r["status"] != "ok" for r in results) has_warning = any( r["status"] == "ok" and r["days_remaining"] is not None and 0 < r["days_remaining"] <= warn_days for r in results ) if has_failure: return 2 if has_warning: return 1 return 0 if args.json: print(json.dumps(results, indent=2)) else: print_report(results, args.warn_days) sys.exit(determine_exit_code(results, args.warn_days)) ``` Refactor `print_report()` so it no longer calls `sys.exit()` internally, preventing output format from controlling security-relevant process behavior. Add automated tests that verify exit codes in both output modes for: - Valid certificates above the warning threshold: exit 0 - Certificates within the warning threshold: exit 1 - Expired or expiring-today certificates: exit 2 - Certificate verification failures: exit 2 - DNS, timeout, connection, and TLS failures: exit 2 ]]>
