T09 · Insecure Skill Coding Practices
- Location
scripts/detect_pii.py:57- Finding
Unmasked PII Is Disclosed Through Standard Output
- Content
View full analysis
Vulnerability Details
File Location:
scripts/detect_pii.py:57-63, 67
Vulnerability Type: Plaintext sensitive-data exposure
Risk Level: MediumVulnerable Code
python results.append({ "category": category, "description": desc, "match": m.group(), "start": m.start(), "end": m.end(), "confidence": 0.95 if category != "ipv4" else 0.6, })python print(json.dumps(results, ensure_ascii=False, indent=2))Technical Analysis
The detector copies every complete regex match into the
matchfield and serializes the resulting JSON to standard output without masking, truncation, or access controls. This affects detected identity-card numbers, telephone numbers, bank-card numbers, email addresses, passport numbers, and IP addresses.This behavior also conflicts with
SKILL.md:37-39, which describes report output as containing a truncated original snippet:markdown 7. **Report** — output redacted PDF + JSON report listing each redaction: - original snippet (truncated), category, page number, bounding box, mode applied.Because standard output is commonly captured by shell redirection, CI systems, agent transcripts, application logs, and monitoring infrastructure, otherwise protected PII can be duplicated into less protected storage.
Attack Path
- A user or automated workflow extracts text from a document containing sensitive information.
- The text is supplied to
detect_pii.pythrough standard input. - A regular expression detects an ID number, bank-card number, phone number, email address, passport number, or IP address.
- The complete sensitive value is assigned to the JSON
matchfield. - The script prints the unmasked value to standard output.
- A terminal logger, CI runner, agent transcript, monitoring service, redirected file, or downstream process captures and retains the plaintext value.
- Anyone with access to that secondary output channel can retrieve the disclosed PII.
...[truncated 617 chars]
- Remediation
View remediation
Remediation Suggestions
- Remove the raw
matchvalue from default output. Return only the category and positional offsets when possible. - If a preview is required, mask or truncate it according to category, such as preserving only the final four digits:
python def mask_match(value: str) -> str: if len(value) <= 4: return "*" * len(value) return "*" * (len(value) - 4) + value[-4:] - Make disclosure of complete values an explicit opt-in mode with a clear warning rather than the default behavior.
- Avoid writing raw sensitive values to application logs, terminal histories, agent transcripts, exception messages, or telemetry.
- If reports must be persisted, use restrictive file permissions, encryption at rest, controlled retention, and secure deletion.
- Add automated tests verifying that JSON output never contains complete sample ID, payment-card, phone, email, or passport values.
- Update the implementation and documentation so the stated truncation guarantee is consistently enforced.
- Remove the raw
