T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/desens_scan.py:54
- Finding
- Detected sensitive content does not produce the documented failure exit status<![CDATA[ ## Vulnerability Details **File Location**: `scripts/desens_scan.py:54-59`; related contract in `SKILL.md:67` **Vulnerability Type**: Fail-open security gate **Risk Level**: High ### Vulnerable Code ```python masked = mask_text(text, hits, a.mask) print(masked) if a.report: rep = [{"type": n, "sample": f[:12] + ("…" if len(f) > 12 else "")} for n, _, f in hits] print("\n--- desens_report ---") print(json.dumps({"count": len(hits), "items": rep}, ensure_ascii=False, indent=2)) ``` The documented exit-status contract at `SKILL.md:67` states that a clean scan returns status `0`, detected issues return status `1`, and usage or environment errors return status `2`. The findings branch above reaches the end of `main()` without calling `sys.exit()`, raising `SystemExit`, or returning a status consumed by the entry point. Python therefore terminates with status `0` after sensitive content is found. ### Technical Analysis Security scanners used as publication or CI gates must fail closed. This implementation successfully identifies and masks findings but communicates success to the calling process. Human-readable output does not compensate for an incorrect process status because automation generally evaluates the exit code rather than parsing localized console messages. This behavior directly contradicts the documented interface and can allow a release pipeline to continue even when the scanner has detected credentials, PII, internal paths, or confidential project identifiers. ### Attack Path 1. A repository or publication candidate contains a value matched by one of the scanner rules. 2. A CI job invokes `desens_scan.py` as a release gate and relies on the documented exit-status contract. 3. The scanner finds the sensitive value, prints masked output, and reaches the end of `main()`. 4. Python exits with status `0`. 5. The CI system interprets the scan as successful and continues packaging or publishing the original artifact. 6. If the pipe ...[truncated 579 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Explicitly terminate with status `1` after processing one or more findings: ```python if hits: masked = mask_text(text, hits, a.mask) print(masked) if a.report: print_report(hits) raise SystemExit(1) ``` - Return status `0` only when no findings exist. - Catch expected file, encoding, and argument errors and return status `2` without exposing raw stack traces. - Refactor `main()` to return an integer and use `raise SystemExit(main())` at the entry point. - Add automated tests asserting all three documented statuses: - clean input returns `0`; - input containing a detectable item returns `1`; - invalid arguments or unreadable input return `2`. - Ensure release workflows scan the exact artifact that will be published or explicitly save and publish the sanitized output. ]]>
