Back to skill

Security audit

military bidding email

Security checks for vulnerabilities and agentic risk

Overview

This skill performs the email-reporting job it advertises, but its broad triggers and no-confirmation send path create a real risk of unintended outbound emails.

Review before installing if accidental outbound email would be costly. Use a dedicated SMTP account or app password, limit EMAIL_TO and EMAIL_CC to intended recipients, test with --to first, and prefer adding a preview/confirmation step plus a safer private lock file before routine use.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
milb_email/fetcher.py:313
Finding

Predictable Temporary Lock File Allows Symlink-Based File Truncation

Content
View full analysis

Vulnerability Details

File Location: milb_email/fetcher.py, lines 313–317 and 335–338
Vulnerability Type: Unsafe temporary lock-file handling
Risk Level: Medium

Vulnerable Code

python
# Lock file prevents concurrent execution
LOCK_FILE = '/tmp/bidding_email.lock'

try:
    lock_fd = open(LOCK_FILE, 'w')
    fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except (IOError, OSError):
    print("[INFO] Another task is already running; skipping this execution")
    sys.exit(0)
python
finally:
    fcntl.flock(lock_fd, fcntl.LOCK_UN)
    lock_fd.close()
    try:
        os.remove(LOCK_FILE)
    except:
        pass

Technical Analysis

The application uses a fixed, predictable filename in the globally writable /tmp directory. It opens that path with mode w, which truncates the referenced file and follows symbolic links before the advisory lock is acquired.

A local attacker can create /tmp/bidding_email.lock as a symbolic link to another file writable by the account that subsequently runs milb-email. When the application opens the lock path, Python follows the symbolic link and truncates the target file.

The later pathname-based removal is also subject to a race: another process could replace the lock pathname after it is opened but before os.remove() executes. The bare except suppresses errors that could otherwise reveal unsafe lock cleanup.

Attack Path

  1. An attacker with local access creates a symbolic link at the predictable path:
    text
    /tmp/bidding_email.lock -> /path/to/user-writable/target
    
  2. A victim runs milb-email under an account that can write to the target.
  3. open('/tmp/bidding_email.lock', 'w') follows the symbolic link.
  4. The target is truncated before flock() provides any protection.
  5. During cleanup, pathname replacement could additionally cause the program to remove an unintended replacement entry.

Impact Assessment

Successful exploitation can corrupt or er ...[truncated 362 chars]

Remediation
View remediation

Remediation Suggestions

  • Store the lock in a private runtime directory owned by the executing user, with permissions such as 0700, rather than directly in shared /tmp.
  • Create the lock atomically with low-level flags such as O_CREAT | O_EXCL | O_NOFOLLOW and mode 0600.
  • Verify with fstat() that the opened object is a regular file and that its owner matches the effective user.
  • Prefer a maintained lock-file library that safely handles creation, ownership, stale locks, and cleanup.
  • If a stable lock inode is used with flock(), avoid unlinking it after every execution. Otherwise, verify that the pathname still identifies the originally opened inode before removal.
  • Replace the bare except during cleanup with specific exception handling and appropriate logging.

Example hardening approach:

python
runtime_dir = Path(os.environ.get("XDG_RUNTIME_DIR", Path.home() / ".cache")) / "milb-email"
runtime_dir.mkdir(mode=0o700, parents=True, exist_ok=True)

lock_path = runtime_dir / "bidding_email.lock"
flags = os.O_RDWR | os.O_CREAT
if hasattr(os, "O_NOFOLLOW"):
    flags |= os.O_NOFOLLOW

lock_fd_raw = os.open(lock_path, flags, 0o600)
lock_fd = os.fdopen(lock_fd_raw, "r+")
fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger phrases include generic language like '发送邮件', '推送报告', and '邮件通知', which can match ordinary conversation and unintentionally activate a capability that sends real emails. Because this skill uses configured SMTP credentials and default recipients, accidental invocation could exfiltrate generated content or cause unauthorized outbound communication without clear user intent.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 38)May include surrounding context.

md
"""
配置加载模块 (milb-email)

从 .env 文件读取配置,所有配置项必填,缺失时直接报错
只加载 EMAIL_* 前缀的配置
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · milb_email/config.py (reported line 6)May include surrounding context.

python
"""
配置加载模块 (milb-email)

从 .env 文件读取配置,所有配置项必填,缺失时直接报错
只加载 EMAIL_* 前缀的配置
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · milb_email/config.py (reported line 28)May include surrounding context.

python
"""
配置加载模块 (milb-email)

从 .env 文件读取配置,所有配置项必填,缺失时直接报错
只加载 EMAIL_* 前缀的配置
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · milb_email/config.py (reported line 65)May include surrounding context.

python
"""
配置加载模块 (milb-email)

从 .env 文件读取配置,所有配置项必填,缺失时直接报错
只加载 EMAIL_* 前缀的配置
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · milb_email/config.py (reported line 70)May include surrounding context.

python
"""
配置加载模块 (milb-email)

从 .env 文件读取配置,所有配置项必填,缺失时直接报错
只加载 EMAIL_* 前缀的配置
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · milb_email/config.py (reported line 35)May include surrounding context.

python
"""
    # 按优先级查找:当前工作目录 → ~/.config/milb-email/
    candidates = [
        Path.cwd() / '.env',
        Path.home() / '.config' / 'milb-email' / '.env',
    ]
    env_path = next((p for p in candidates if p.exists()), None)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · milb_email/config.py (reported line 36)May include surrounding context.

python
"""
    # 按优先级查找:当前工作目录 → ~/.config/milb-email/
    candidates = [
        Path.cwd() / '.env',
        Path.home() / '.config' / 'milb-email' / '.env',
    ]
    env_path = next((p for p in candidates if p.exists()), None)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documentation explicitly describes fetching data, generating reports, and sending them via SMTP with an attachment, but does not warn that running the tool causes outbound transmission of potentially sensitive procurement data. In a security-sensitive military procurement context, lack of explicit disclosure and consent increases the risk of unintended data exfiltration, misdelivery, or operator misuse.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill advertises capabilities that imply reading configuration files, writing generated Excel reports, and invoking local binaries, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this makes the operational envelope ambiguous and can allow the skill to access files or execute commands beyond what a reviewer or user would reasonably expect.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The quick-start usage explains how to send reports but does not prominently warn that the skill will use preconfigured SMTP credentials and default recipients from the environment. In practice, a user may believe they are generating a local report or running a harmless test, when the skill is actually capable of transmitting data externally.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This Python file contains module documentation and later user-facing output entirely in Chinese, indicating the skill is designed around a fixed language. The policy forbids forcing a specific language without user opt-in unless the locale constraint is clearly documented and justified, which is not present here.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring says all configuration items are mandatory and missing values cause an error. In practice, REQUIRED_CONFIG_KEYS covers only a subset of fields, while get_email_config() also accesses EMAIL_RECIPIENT_NAME, EMAIL_SENDER_NAME, EMAIL_SUBJECT_PREFIX, and EMAIL_BODY_INTRO without including them in the declared required list. This makes the documented requirement inconsistent with the actual validation model.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The module documentation at L007 states that only configuration items with the EMAIL_* prefix are loaded. However, load_config() parses and stores any key=value pair it encounters in the selected .env file, with no prefix check. This is an active contradiction between the documented intent and the implemented behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The file-level description is written entirely in Chinese ("军工采购商机邮件发送工具") with no indication that language selection is configurable or optional. This can violate language/locale policy when a skill implicitly requires a specific language without offering user choice or documenting a justified locale restriction.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This Python file contains the main description, usage examples, CLI help text, and runtime messages exclusively in Chinese. Under the natural-language policy rule, forcing a specific language without user opt-in can be a locale policy violation when no alternative language choice is provided.

Content

No source excerpt is available for this finding.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency on requests is unpinned, so builds may resolve to different versions over time, including versions affected by known advisories. In a tool that fetches procurement data and sends email, a vulnerable HTTP client could expose credentials, mishandle TLS/session validation, or leak sensitive request data if an affected release is installed.

Content

No source excerpt is available for this finding.

Unverifiable Dependency: pandas has 1 known advisory(ies) (CVE-2020-13091 (** DISPUTED ** pandas through 1.0.3 can unserialize and execute commands from an)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding

The dependency on pandas is also unpinned, which makes the installed version unverifiable and can allow deployment of a release with known security issues. Although the cited pandas advisory is narrower and often requires unsafe deserialization workflows, this package generates Excel reports, so spreadsheet-processing code increases the relevance of carefully controlling the exact version used.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.