T09 · Insecure Skill Coding Practices
- Location
SKILL.md:11- Finding
SMTP Credentials May Be Stored in a Tracked Skill Definition
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md:11-30; supporting implementation inscripts/send_email.py:83-118andscripts/send_email.py:194-205
Vulnerability Type: Credential exposure through insecure configuration storage
Risk Level: MediumVulnerable Code
SKILL.md:11-30:dotenv # Provider preset: 126, qq, sina, aliyun, custom EMAIL_PROVIDER=126 # If EMAIL_PROVIDER=custom, fill these yourself. SMTP_HOST=smtp.126.com SMTP_PORT=465 # true for SMTPS on port 465, false for STARTTLS on port 587. SMTP_USE_SSL=true # Usually the mailbox address. SMTP_USERNAME=your_email_username # 126 / QQ commonly use an SMTP authorization code here. # Sina / Aliyun may use the mailbox password depending on account settings. SMTP_PASSWORD=your_smtp_secret FROM_EMAIL=your_email_username FROM_NAME=your_name # Seconds SMTP_TIMEOUT=30The sending script parses the embedded block from the Skill file and merges it into the active configuration in
scripts/send_email.py:83-118:python def load_embedded_skill_config(path: Path) -> dict[str, str]: if not path.exists(): return {} lines = path.read_text(encoding="utf-8-sig").splitlines() heading_index = next( (index for index, line in enumerate(lines) if line.strip() == EMBEDDED_CONFIG_HEADING), None, ) if heading_index is None: return {} fence_start = None for index in range(heading_index + 1, len(lines)): stripped = lines[index].strip() if stripped.startswith("```dotenv") or stripped.startswith("```env") or stripped == "```": fence_start = index + 1 break if stripped.startswith("## "): return {} if fence_start is None: return {} block_lines: list[str] = [] for index in range(fence_start, len(lines)): stripped = lines[index].strip() if stripped == "```": break block_lines.append(lines[index]) if not block_lines: ...[truncated 2925 chars]- Remediation
View remediation
Remediation Suggestions
- Remove support for loading
SMTP_PASSWORDfromSKILL.md. Skill documentation should contain placeholders only and must never function as a runtime secret source. - Read the password from a process environment variable, operating-system credential manager, CI/CD secret store, or dedicated secrets-management service.
- If
.envremains supported, add.envand similarly named local secret files to.gitignore, document that they must never be committed, and restrict their filesystem permissions. - Separate non-sensitive provider defaults from secret values. Hosts, ports, and TLS modes may remain in tracked configuration, while authentication credentials must be supplied at runtime.
- Prefer provider-issued, revocable, application-specific SMTP authorization codes over normal mailbox passwords.
- Remove password previews from dry-run output. Even partial disclosure can reveal credential length and fragments and provides little operational value.
- Add secret-scanning checks to pre-commit and CI workflows to detect SMTP passwords, authorization codes, and accidentally populated configuration examples.
- If a real credential has ever been placed in
SKILL.md, revoke or rotate it immediately and remove it from repository history and distributed artifacts.
- Remove support for loading
