Back to skill

Security audit

tech-news-brief

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed tech-news automation workflow, but it can automatically download, package, and email files using stored SMTP credentials with limited user-control safeguards.

Install only if you intend this skill to send generated report archives by email. Configure a dedicated SMTP account or app password, confirm SMTP_TO before use, avoid passing passwords on the command line, constrain REPORTS_DIR and download output paths, and treat the Chinese/China-focused writing style as intentional editorial framing.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/smtp_send.py:21
Finding

SMTP Credentials Can Be Exposed Through Command-Line Arguments or an Attacker-Controlled SMTP Server

Content
View full analysis

Vulnerability Details

File Location: scripts/smtp_send.py, lines 3, 19-22, 48-50, 74-76, and 97-107
Vulnerability Type: Credential exposure and insufficient endpoint validation
Risk Level: High

Vulnerable Code

python
# Usage permits passing the password directly on the command line:
# python3 scripts/smtp_send.py <zip_path> <recipient> [--subject title] [--sender sender] [--passwd password]

DEFAULT_SENDER = os.environ.get(
    "SMTP_SENDER",
    sys.argv[2] if len(sys.argv) > 2 else ""
)
DEFAULT_PASSWD = os.environ.get("SMTP_PASSWD", "")
SMTP_SERVER = os.environ.get("SMTP_SERVER", "smtp.qq.com")
SMTP_PORT = int(os.environ.get("SMTP_PORT", "465"))

def send_email_with_attachment(
    zip_path: str,
    recipient: str,
    subject: str = None,
    sender: str = None,
    password: str = None,
    server: str = SMTP_SERVER,
    port: int = SMTP_PORT
) -> bool:
    subject = subject or f"科技简报-{os.path.basename(zip_path).replace('.zip','')}"
    sender = sender or DEFAULT_SENDER
    password = password or DEFAULT_PASSWD

    # ...

    smtp = smtplib.SMTP_SSL(server, port)
    smtp.login(sender, password)
    smtp.sendmail(sender, recipient, msg.as_string())
    smtp.quit()

# ...

password = None
i = 3
while i < len(sys.argv):
    if sys.argv[i] == "--subject" and i + 1 < len(sys.argv):
        subject = sys.argv[i + 1]
        i += 2
    elif sys.argv[i] == "--sender" and i + 1 < len(sys.argv):
        sender = sys.argv[i + 1]
        i += 2
    elif sys.argv[i] == "--passwd" and i + 1 < len(sys.argv):
        password = sys.argv[i + 1]
        i += 2
    else:
        i += 1

Technical Analysis

The script supports passing the QQ mailbox authorization code through the --passwd command-line option. Command-line arguments may be retained in shell history, process-monitoring logs, job-runner telemetry, or ...[truncated 2658 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove the --passwd option entirely. Retrieve the authorization code only from a protected secret manager, an appropriately scoped environment secret, or an interactive non-echoing prompt.
  2. Fix the SMTP endpoint to smtp.qq.com when QQ Mail is the only supported provider.
  3. If endpoint configurability is required, enforce an explicit allowlist of approved SMTP hostnames and ports.
  4. Reject IP-literal destinations, unexpected ports, malformed hostnames, and hosts outside the approved provider set.
  5. Keep TLS certificate and hostname validation enabled. Consider provider-specific certificate or public-key pinning where operationally feasible.
  6. Use a dedicated mailbox authorization code with the narrowest available permissions rather than a primary account password.
  7. Rotate the authorization code immediately if it has previously been passed through --passwd or exposed in logs.
  8. Redact secrets and sensitive SMTP responses from job logs and monitoring systems.
  9. Correct DEFAULT_SENDER: falling back to sys.argv[2] currently treats the recipient argument as the sender when SMTP_SENDER is absent. Require an explicit, validated sender instead.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/download.py:27
Finding

Caller-Controlled Download Path Permits Arbitrary File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/download.py, lines 17, 27-29, 65-67, and 75-78
Vulnerability Type: Unrestricted file write and path traversal
Risk Level: Medium

Vulnerable Code

python
REPORTS_DIR = os.environ.get(
    "REPORTS_DIR",
    "/home/sml/workspace/InternetInformation"
)

def download_with_firecrawl(
    url: str,
    output_path: str,
    api_key: str = FIRECRAWL_API_KEY,
) -> bool:
    import urllib.request
    import urllib.error

    output_dir = os.path.dirname(output_path)
    if output_dir:
        os.makedirs(output_dir, exist_ok=True)

    # The remote service returns Markdown derived from the requested URL.
    payload = json.dumps({
        "url": url,
        "formats": ["markdown"],
        "onlyMainContent": True
    }).encode("utf-8")

    # ...

    markdown = raw.get("data", {}).get("markdown", "")

    with open(output_path, "w", encoding="utf-8") as f:
        f.write(markdown)

    char_count = len(markdown)
    print(f"下载成功: {output_path}")
    print(f"字数统计: {char_count} 字", file=sys.stderr)
    return True

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(
            "Usage: python3 scripts/download.py <URL> <output path>",
            file=sys.stderr
        )
        sys.exit(1)

    url = sys.argv[1]
    output_path = sys.argv[2]

    ok = download_with_firecrawl(url, output_path)
    sys.exit(0 if ok else 1)

Technical Analysis

The output filename is taken directly from sys.argv[2]. The script creates parent directories and opens the resulting path in write mode without normalization, traversal checks, symlink checks, file-type restrictions, or enforcement that the destination remains under REPORTS_DIR.

Opening a path with mode "w" truncates an existing file before writing the remotely derived Markdown. Consequently, any file writable by the p ...[truncated 2391 chars]

Remediation
View remediation

Remediation Suggestions

  1. Resolve REPORTS_DIR and the requested destination with pathlib.Path.resolve().
  2. Require the resolved destination to be a descendant of the resolved report directory:
    python
    from pathlib import Path
    
    reports_root = Path(REPORTS_DIR).resolve()
    destination = Path(output_path).resolve()
    
    if destination == reports_root or reports_root not in destination.parents:
        raise ValueError("Output path must remain under REPORTS_DIR")
    
  3. Do not allow callers to supply an arbitrary absolute path. Prefer accepting only a generated filename or article identifier and construct the full destination internally.
  4. Restrict outputs to the expected temporary Markdown naming convention, such as *-temp.md.
  5. Reject path traversal components, device files, pipes, sockets, and unexpected filename extensions.
  6. Prevent symlink attacks by verifying each path component and opening the destination with platform-appropriate no-follow protections.
  7. Write to a securely created temporary file in the destination directory, validate the response, and atomically rename it into place.
  8. Avoid overwriting existing files unless explicitly requested and authorized. Use exclusive creation where appropriate.
  9. Apply a response-size limit before writing to prevent excessive disk consumption.
  10. Run the downloader under a dedicated low-privilege account that cannot modify application code, Agent configuration, credentials, or startup files.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (17)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill instructs the agent to read SMTP credentials from environment variables and send an email, but it does not require interactive confirmation of the recipient or the send action. In an agent context, this creates a clear unauthorized data egress path using stored secrets, especially if the skill is triggered accidentally or manipulated to include unintended content.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
86% confidence
Finding

The skill includes a raw shell deletion command with interpolated path components, creating a dangerous pattern if variables are malformed, attacker-influenced, or expanded unexpectedly. Even if intended only for cleanup, destructive shell operations in agent skills increase the risk of deleting unintended files when path validation is absent.

Content

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

发送成功后立即删除 zip 包:

bash
rm {REPORTS_DIR}/{YYYYMMDD}.zip

仅删除 zip 包,保留 {YYYYMMDD}/ 目录下的原始 md/docx 文件。

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

The instruction to manually remove a lock file encourages destructive filesystem actions against a fixed operational path without validation or safety checks. In an agent setting, even seemingly narrow delete commands normalize unsafe tool use and can be abused or misapplied if the environment differs, symlinks are present, or path assumptions are wrong.

Content

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

md
### Cron 规范
- **已知问题**:cron 任务经常只完成 Phase 2 就中断,不执行 Phase 3-8。修复方式:手动补跑。
- **`.tick.lock` 残留**:上次超时中断未释放锁,`rm ~/.hermes/profiles/scienlot/cron/.tick.lock`。
- **日志未保存**:cron output 目录无日志文件时,检查 `{REPORTS_DIR}/` 是否有生成文件。

---

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README describes the tool as producing Chinese tech news briefs, and later requirements specify Chinese summaries and formatting rules without mentioning any user opt-in or alternative language support. This is a natural-language locale constraint that may violate language-choice policy unless explicitly justified or made optional.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README advertises automatic ZIP packaging and email delivery but does not prominently warn that generated files will be transmitted to a configured recipient. This creates a meaningful risk of unintended data exfiltration, especially in an agent context where users may trigger the skill conversationally without realizing it performs outbound transmission.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases are broad, natural-language requests like asking for recent AI news, which can easily overlap with ordinary user conversation. In an agent environment, this increases the chance of accidental invocation of a workflow that performs network access, file generation, compression, and email sending without sufficiently explicit user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

描述要求将“中文或其他外文科技新闻”整理为特定“xx班”规范的中文原文稿、中文摘要和 DOCX 文件,默认固定输出形式与语言,但没有说明用户可选择其他语言或选择退出该本地化约束。对跨语言新闻处理而言,这构成未经用户选择的语言/locale 强制。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill description advertises end-to-end email delivery but does not warn that generated content and attachments will be sent externally. Users may believe they are only requesting summarization, while the skill is designed to exfiltrate results to a configured recipient as part of the default flow.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger phrases are broad, natural-language requests that overlap with ordinary conversation, making accidental activation plausible. Because this skill can proceed into downloading content, generating files, and emailing outputs, a false trigger can cause unintended external actions without the user explicitly invoking the workflow.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This markdown file is written entirely in Chinese and presents all guidance, headings, and usage notes only in that language. Under the policy rule for natural-language violations, forcing a specific language without user opt-in or a documented region-specific justification is a reportable issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The guide hard-codes a 'China-first' evaluative lens for China-related technology news, steering outputs toward a predetermined geopolitical perspective without user consent. This introduces systematic bias and can cause the agent to generate politically slanted summaries that misrepresent source material or override user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Requiring all China-related news to include impact on China's industry or strategic environment forces a fixed national framing even when irrelevant to the source or user task. This can skew summaries, suppress neutrality, and create covert policy influence in downstream outputs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The instruction to preserve '我方立场' ('our position') embeds an implicit organizational or national stance into summarization, encouraging the model to inject viewpoint-driven framing rather than objective synthesis. In a writing guide, this is especially risky because it scales biased behavior across all outputs produced under the skill.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/download.py (reported line 39)May include surrounding context.

python
}).encode("utf-8")

    req = urllib.request.Request(
        "https://api.firecrawl.dev/v2/scrape",
        data=payload,
        headers={
            "Authorization": f"Bearer {api_key}",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script accepts the SMTP password via the --passwd command-line flag, which can expose the credential through shell history, process listings, audit logs, and job runners. In this skill context, the script is intended for operational use and may be run on shared systems or CI-like environments, making accidental credential disclosure more likely.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The manifest names the skill as "tech-news-brief(整合版)", mixing English with Chinese and implying a locale-specific presentation, but the file provides no indication that users can opt into that language or that the locale constraint is justified. Under the policy, language-specific behavior should either offer user choice or clearly document why the locale is required.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file's comments, usage text, docstrings, and runtime messages are presented only in Chinese, which imposes a specific language on users without any opt-in or alternative locale support. This matches the language/locale policy concern because the script does not offer language choice or document that it is intentionally limited to a Chinese-only audience.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.