Back to skill

Security audit

Auto Sec Blogger Repo

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims, but it can automatically publish to GitHub and has concrete implementation risks that could expose or overwrite unintended files.

Install only if you are comfortable giving this skill access to GLM, Notion, and a GitHub Pages repository. Use a test Notion database and clean temporary blog clone first, avoid personal or production .env files, disable the background publisher unless explicitly needed, pin Mermaid CLI locally, sanitize category paths, and replace git add . with staging only the generated post files.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T08 · Insecure Dependencies

Error
Location
scripts/notion_publisher.py:79
Finding

Unpinned Mermaid Package Is Downloaded and Executed at Runtime

Content
View full analysis

Vulnerability Details

File Location: scripts/notion_publisher.py:79-86
Vulnerability Type: Runtime execution of an unpinned third-party package
Risk Level: High

Vulnerable Code

python
result = subprocess.run([
    'npx', '-y', '@mermaid-js/mermaid-cli',
    '-i', mmd_file,
    '-o', str(output_file),
    '-b', 'transparent',
    '-s', '2'  # scale
], capture_output=True, timeout=30, text=True)

Technical Analysis

Mermaid rendering invokes npx -y @mermaid-js/mermaid-cli without an exact package version, lockfile, or integrity verification. When the package is unavailable locally, npx can retrieve the current package release from the configured npm registry and immediately execute it without interactive confirmation.

The executable payload can therefore change after the Skill has been reviewed. A compromised package release, npm account, registry, dependency, or local registry configuration could cause arbitrary JavaScript to execute under the identity of the Agent process.

Rendering Mermaid diagrams is necessary for the declared functionality, but downloading and executing a mutable package during normal processing exceeds the minimum safe privilege required. A reviewed and locally installed renderer would provide the same functionality with materially less risk.

Attack Path

  1. An attacker compromises the Mermaid CLI package, one of its dependencies, its publisher account, or the npm registry resolution path.
  2. The Agent processes content that invokes _convert_mermaid_to_image.
  3. npx -y resolves and downloads the mutable package version without requesting approval.
  4. Package installation hooks or CLI initialization code execute locally.
  5. Malicious code inherits the Agent process environment and filesystem permissions.
  6. The code can access application credentials, modify the blog repository, alter generated content, or initiate additional network connections.

...[truncated 685 chars]

Remediation
View remediation

Remediation Suggestions

  1. Install Mermaid CLI during a controlled deployment phase rather than during article processing.
  2. Pin an exact reviewed version, for example @mermaid-js/mermaid-cli@X.Y.Z.
  3. Commit and enforce a package lockfile with npm ci.
  4. Verify registry origin and lockfile integrity in CI.
  5. Invoke a fixed local executable such as node_modules/.bin/mmdc; do not use runtime npx -y.
  6. Disable package lifecycle scripts during installation where compatible, using npm ci --ignore-scripts.
  7. Run rendering in a sandbox or container without API credentials and with write access limited to the image output directory.
  8. Apply resource, network, and execution time limits to the renderer.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publisher_git.py:99
Finding

Remote Notion Category Allows Filesystem Path Traversal

Content
View full analysis

Vulnerability Details

File Location: scripts/publisher_git.py:99-129
Additional Location: scripts/run_git_publisher.py:70-106
Vulnerability Type: Path traversal through unvalidated remote metadata
Risk Level: High

Vulnerable Code

python
category = props.get('카테고리', {}).get('select', {}).get('name', '보안')
tags_objs = props.get('테그', {}).get('multi_select', [])
tags_list = [tag.get('name', '') for tag in tags_objs]

content = self.notion_publisher.get_page_content(page_id)
if not content:
    content = "이 글은 LLM을 기반으로 작성되었습니다."

slug = self._slugify(title)
post_dir = self.blog_repo_path / "content" / "post" / category / slug
post_dir.mkdir(parents=True, exist_ok=True)

md_file = post_dir / "index.md"

front_matter = f"""---
title: "{title}"
date: {datetime.now().strftime("%Y-%m-%dT%H:%M:%S+09:00")}
draft: false
tags:
{chr(10).join(f'  - "{tag}"' for tag in tags_list)}
categories:
  - "{category}"
---

{content}
"""
with open(md_file, 'w', encoding='utf-8') as f:
    f.write(front_matter)

The same unsafe construction is present in scripts/run_git_publisher.py:

python
category = props.get('카테고리', {}).get('select', {}).get('name', '보안')
tags_objs = props.get('테그', {}).get('multi_select', [])
tags_list = [tag.get('name', '') for tag in tags_objs]

# Create slug
import re
if re.search(r'[가-힣]', title):
    slug = title.replace(' ', '-')
else:
    slug = title.lower().replace(' ', '-')
slug = re.sub(r'[^\w\-]', '', slug)

# Create directory
post_dir = blog_repo_path / "content" / "post" / category / slug
post_dir.mkdir(parents=True, exist_ok=True)

# Create markdown file
md_file = post_dir / "index.md"

Technical Analysis

The category value originates from a Notion page and is inserted directly into a filesystem path. Although the title is converted to a restricted slug, the category is neither sanitized nor mapped ...[truncated 2055 chars]

Remediation
View remediation

Remediation Suggestions

  1. Define a strict category allowlist, such as Security, AI, DevOps, CVE, and IT.
  2. Map Notion display names to fixed local directory names rather than using remote text directly.
  3. Reject category values containing /, \, null bytes, . path components, or absolute-path syntax.
  4. Resolve and verify the destination before creating directories:
python
allowed_root = (self.blog_repo_path / "content" / "post").resolve()
category_dir = CATEGORY_PATHS.get(category)
if category_dir is None:
    raise ValueError("Unsupported category")

post_dir = (allowed_root / category_dir / slug).resolve()
if not post_dir.is_relative_to(allowed_root):
    raise ValueError("Destination escapes the post directory")
  1. Apply the same centralized validation to both publisher_git.py and run_git_publisher.py.
  2. Reject symlinked destination directories or securely verify each resolved component.
  3. Log rejected values without writing content and require manual intervention.
  4. Restrict the publisher process to write only within the intended repository directory.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publisher_git.py:132
Finding

Repository-Wide Git Staging Can Publish Unrelated Sensitive Files

Content
View full analysis

Vulnerability Details

File Location: scripts/publisher_git.py:132-139
Additional Location: scripts/run_git_publisher.py:110-125
Vulnerability Type: Overbroad Git staging and unintended data disclosure
Risk Level: Medium

Vulnerable Code

python
def _git_commit(self, title: str) -> None:
    # cwd 변경 대신 subprocess의 cwd 파라미터 사용 권장
    cmd = ['git', 'add', '.']
    subprocess.run(cmd, cwd=self.blog_repo_path, check=True, capture_output=True)
    
    cmd = ['git', 'commit', '-m', f"feat: 블로그 글 추가 - {title}"]
    subprocess.run(cmd, cwd=self.blog_repo_path, check=True, capture_output=True)

The standalone publisher repeats the behavior:

python
# Git operations
print("\n[Git] Adding changes...")
subprocess.run(['git', 'add', '.'],
              cwd=blog_repo_path,
              check=True,
              capture_output=True)

print("[Git] Committing...")
subprocess.run(['git', 'commit', '-m', f"feat: 블로그 글 추가 - {title}"],
              cwd=blog_repo_path,
              check=True,
              capture_output=True)

print("[Git] Pushing to remote...")
subprocess.run(['git', 'push', 'origin', 'main'],
              cwd=blog_repo_path,
              check=True,
              capture_output=True)

Technical Analysis

The publisher creates one article but executes git add ., which stages every changed or untracked file below the repository working directory. It then commits and pushes the entire staged set without checking which paths were included.

This violates least-privilege principles at the repository level. The publishing operation only needs to stage the newly generated post, but it instead receives authority over all current working-tree changes.

No command injection is present because subprocess arguments are passed as a list without shell=True. The security issue is the excessive scope of the Git operation rather than shell interpretat ...[truncated 1340 chars]

Remediation
View remediation

Remediation Suggestions

  1. Stage only the generated article path:
python
relative_post = md_file.resolve().relative_to(self.blog_repo_path.resolve())
subprocess.run(
    ['git', 'add', '--', str(relative_post)],
    cwd=self.blog_repo_path,
    check=True,
    capture_output=True
)
  1. Return the generated file path from _create_blog_post and pass that exact path to the commit function.
  2. Before committing, inspect git diff --cached --name-only and reject any path other than the expected generated files.
  3. Use git status --porcelain before staging and abort when unrelated modifications are present.
  4. Maintain a restrictive .gitignore, but do not rely on it as the only protection.
  5. Reject symlinks and ensure the staged file resolves inside the approved content directory.
  6. Consider publishing from a clean temporary clone or isolated worktree so unrelated local changes cannot enter the commit.
  7. Require explicit confirmation if more than the expected article files would be pushed.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (79)

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 · README.md (reported line 28)May include surrounding context.

⚙️ 환경 설정

bash
# .env 파일 생성
cat > .env << 'ENVEOF'
GLM_API_KEY=your-glm-api-key
NOTION_API_KEY=your-notion-api-key

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 · README.md (reported line 29)May include surrounding context.

⚙️ 환경 설정

bash
# .env 파일 생성
cat > .env << 'ENVEOF'
GLM_API_KEY=your-glm-api-key
NOTION_API_KEY=your-notion-api-key

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 69)May include surrounding context.

⚙️ 환경 설정

bash
# .env 파일 생성
cat > .env << 'ENVEOF'
GLM_API_KEY=your-glm-api-key
NOTION_API_KEY=your-notion-api-key

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 · scripts/config.py (reported line 45)May include surrounding context.

python
## ⚙️ 환경 설정

```bash
# .env 파일 생성
cat > .env << 'ENVEOF'
GLM_API_KEY=your-glm-api-key
NOTION_API_KEY=your-notion-api-key

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 · scripts/config.py (reported line 50)May include surrounding context.

python
## ⚙️ 환경 설정

```bash
# .env 파일 생성
cat > .env << 'ENVEOF'
GLM_API_KEY=your-glm-api-key
NOTION_API_KEY=your-notion-api-key

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 · scripts/config.py (reported line 46)May include surrounding context.

python
# 2. 환경 변수 로드
# 프로젝트 루트의 .env 파일 우선적으로 로드
env_path = PROJECT_ROOT / ".env"
if env_path.exists():
    load_dotenv(env_path, override=True)
else:

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 · scripts/config.py (reported line 51)May include surrounding context.

python
# 2. 환경 변수 로드
# 프로젝트 루트의 .env 파일 우선적으로 로드
env_path = PROJECT_ROOT / ".env"
if env_path.exists():
    load_dotenv(env_path, override=True)
else:

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 · scripts/publisher_git.py (reported line 168)May include surrounding context.

python
# 2. 환경 변수 로드
# 프로젝트 루트의 .env 파일 우선적으로 로드
env_path = PROJECT_ROOT / ".env"
if env_path.exists():
    load_dotenv(env_path, override=True)
else:

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The script loads secrets from a hardcoded absolute .env path and forces override=True, which can silently replace safer runtime configuration with whatever is present on disk. In an agent setting this is more dangerous because it assumes access to a specific developer machine path and may expose or misuse local credentials unexpectedly.

Content

Scanner excerpt · scripts/run_git_publisher.py (reported line 13)May include surrounding context.

python
import importlib.util
from dotenv import load_dotenv

# Load .env
env_path = '/Users/nabang/Documents/OpenClaw/.env'
load_dotenv(env_path, override=True)

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Using a local .env file as a credential source is not inherently malicious, but in this implementation it couples the script to a sensitive local path and encourages credential handling outside managed secret storage. In automated or shared environments, this can lead to accidental secret exposure, misuse of developer credentials, or execution with unintended accounts.

Content

Scanner excerpt · scripts/run_git_publisher.py (reported line 14)May include surrounding context.

python
from dotenv import load_dotenv

# Load .env
env_path = '/Users/nabang/Documents/OpenClaw/.env'
load_dotenv(env_path, override=True)

# Load NotionPublisher directly

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 · scripts/run_pipeline.py (reported line 16)May include surrounding context.

python
# 환경 변수 로드
from dotenv import load_dotenv
env_file = Path.home() / '.openclaw' / 'workspace' / '.env'
load_dotenv(env_file)

# Security News Module 경로 추가

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The script loads environment variables from a hardcoded absolute path to a local .env file and forces override=True, which can silently replace existing environment configuration. In a shared or automated environment, this increases the risk of unintended credential use, secret exposure through misuse of local developer configuration, and brittle behavior tied to one user's filesystem.

Content

Scanner excerpt · scripts/test_full_pipeline.py (reported line 11)May include surrounding context.

python
from pathlib import Path
from dotenv import load_dotenv

# Load .env
env_path = '/Users/nabang/Documents/OpenClaw/.env'
load_dotenv(env_path, override=True)

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Calling load_dotenv on a developer-specific .env path with override=True can cause the script to ingest credentials from an unintended local file and overwrite safer runtime-provided values. This is risky because the script then uses those secrets to interact with external services like Notion, potentially operating on the wrong account, workspace, or data set.

Content

Scanner excerpt · scripts/test_full_pipeline.py (reported line 12)May include surrounding context.

python
from dotenv import load_dotenv

# Load .env
env_path = '/Users/nabang/Documents/OpenClaw/.env'
load_dotenv(env_path, override=True)

# Load NotionPublisher directly

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README states that the AI generates blog posts in Korean ('한국어 블로그 포스트 자동 생성'). This is a natural-language locale constraint, but the document does not indicate that users can opt into another language or that the Korean-only behavior is required for a clearly region-specific purpose.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
88% confidence
Finding

The skill describes autonomous collection, generation, and auto-deployment behavior, which can materially affect external systems and public content with limited friction. Even with a Human-in-the-Loop note, the top-level description emphasizes automation and does not make the approval gate and failure-safe conditions sufficiently strong or explicit.

Content

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

md
---
name: auto-sec-blogger
description: AI-powered security blog automation system (identical to github.com/rebugui/intelligence-agent). Collects news from Google News, arXiv, HackerNews → generates blog posts with GLM-4.7 → publishes to Notion → auto-deploys to GitHub Pages via Git. Features Human-in-the-Loop approval workflow. Use when you want to automate blog writing, news collection, or content generation with the exact functionality of the original intelligence-agent repository. Triggers: "블로그 글 작성", "보안 뉴스 발행", "깃헙 블로그 발행", "intelligence agent", "지능형 에이전트", "자동 글쓰기".
---

# Intelligence Agent

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The trigger list is overly broad and includes generic phrases for writing automation, which can cause the skill to activate in situations beyond the user's likely intent. In this skill's context, accidental invocation is more dangerous because the workflow can send content to external services and eventually publish to GitHub Pages.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The description advertises collection, LLM generation, Notion storage, and GitHub Pages publishing but does not prominently warn that user content, metadata, and possibly repository material will be transmitted to third-party services. This lack of disclosure undermines informed consent and can lead to unintended data exposure and public publication.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The skill is explicitly configured to transmit data to an external LLM API endpoint, which creates a real data-exposure surface. In this context, the risk is heightened because generated content may include scraped article text, user-supplied prompts, metadata, and possibly unpublished drafts sent outside the local environment.

Content

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

md
# GLM API
GLM_API_KEY=your_glm_api_key
GLM_BASE_URL=https://api.z.ai/api/coding/paas/v4

# Notion
NOTION_API_KEY=ntn_xxx

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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 · references/architecture.md (reported line 182)May include surrounding context.

md
class NotionPublisher:
    def create_article(self, blog_post):
        # Notion API 호출
        response = requests.post(
            f"https://api.notion.com/v1/pages",
            headers={
                "Authorization": f"Bearer {NOTION_API_KEY}",

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring states that it collects from distinct sources including Geeknews, implying a real Geeknews source integration. However, the only Geeknews implementation at L248-L250 simply calls fetch_hadaio(), so the documented source list contradicts the actual behavior rather than merely omitting detail.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The Google News RSS URL forces hl=en-US, gl=US, and ceid=US:en, which imposes a specific language and regional locale in the skill behavior. This is a natural-language policy concern because the file does not offer a user choice or explain why US English is required.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The method is named and documented as 'Geeknews collection', which conveys that it fetches data from Geeknews. In reality it directly returns fetch_hadaio(max_results), causing the function's stated intent to diverge from what the code does.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The module creates filesystem directories as a side effect of import, which means merely importing configuration code modifies the host environment. In agent or plugin contexts this can be risky because untrusted or unexpected imports can write to arbitrary locations derived from OPENCLAW_ROOT, potentially causing unwanted persistence, clutter, or writes into sensitive paths if the environment is manipulated.

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/config.py (reported line 79)May include surrounding context.

python
PROJECT_DATABASE_ID = get_env("INTELLIGENCE_PROJECT_DATABASE_ID")

# LLM Config
GLM_BASE_URL = get_env("INTELLIGENCE_LLM_BASE_URL", "https://api.z.ai/api/coding/paas/v4/")
GLM_MODEL = get_env("INTELLIGENCE_LLM_MODEL", "glm-5")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code file describes and initiates automatic detection and publication of Notion content, including auto-start at boot and continuous monitoring. While logging is present, there is no confirmation prompt or explicit user disclosure in the file about the risk of unattended publication affecting user data or repository state.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.