Back to skill

Security audit

App Rank Monitor

Security checks for vulnerabilities and agentic risk

Overview

This skill performs the advertised rank-monitoring workflow, but it ships real DingTalk credentials/default notification targets and uses plaintext login/session storage plus persistent browser automation.

Do not install this version as-is. Require the publisher to remove and rotate the committed DingTalk secrets, ship only example config files, store Diandian credentials/cookies in a protected secret store, avoid long-lived CDP browser sessions, and make any scheduled execution clearly opt-in with uninstall steps.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
config/dingtalk.yaml:5
Finding

Hardcoded DingTalk application secret and webhook access token

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
tools/diandian_auto_login.py:23
Finding

Account passwords and reusable session cookies are persisted in plaintext

Content
View full analysis
Remediation
View remediation

T06 · System Persistence

Error
Location
tools/keep_browser_alive.sh:4
Finding

Long-lived authenticated browser exposes an unauthenticated Chrome DevTools control interface

Content
View full analysis
/tmp/diandian_browser.log 2>&1 & BROWSER_PID=$! echo $BROWSER_PID > /tmp/diandian_browser.pid echo "Browser is running in the background" echo "The browser will not be closed" ``` The scraper then attaches to the existing browser and reuses its first context: ```python try: browser = await p.chromium.connect_over_cdp('http://127.0.0.1:9222') print("Connected to existing browser on CDP port 9222") if len(browser.contexts) > 0: context = browser.contexts[0] print("Using existing browser context") else: context = await browser.new_context( viewport={'width': 1920, 'height': 1080} ) except Exception as e: ... ``` ### Technical Analysis The browser is deliberately launched through `nohup`, uses a persistent profile containing authenticated state, enables Chrome DevTools Protocol on TCP port 9222, and is intentionally kept running after the task completes. Chrome DevTools Protocol provides high-privilege browser automation capabilities. A process able to reach the endpoint can inspect pages, execute JavaScript in page contexts, read non-HttpOnly storage, navigate tabs, initiate requests under active sessions, and interact with authenticated sites. The code does not implement authentication for CDP. The scraper u ...[truncated 1902 chars]
Remediation
View remediation

T06 · System Persistence

Warning
Location
docs/定时任务配置说明.md:22
Finding

Documented launchd persistence runs a credentialed data-export workflow across sessions

Content
View full analysis
Label com.cfans.diandian-daily-fetch ProgramArguments /bin/bash /Users/cfans/.openclaw/workspace/skills/app-rank-monitor/tools/daily_fetch_all.sh StartCalendarInterval Hour 7 Minute 30 ``` ```bash # Load service launchctl load ~/Library/LaunchAgents/com.cfans.diandian-daily-fetch.plist # Unload service launchctl unload ~/Library/LaunchAgents/com.cfans.diandian-daily-fetch.plist ``` The scheduled script starts or reuses the persistent debugging browser and invokes the scraper with automatic DingTalk delivery: ```bash if ! pgrep -f "chrome.*9222" > /dev/null; then bash "$SCRIPT_DIR/keep_browser_alive.sh" >> "$LOG_FILE" 2>&1 fi $VENV_PYTHON "$SCRIPT_DIR/diandian_connect.py" \ --platform "$platform" $extra_args --force ``` ### Technical Analysis The project documents registration of a macOS LaunchAgent that survives individual Skill executions and runs every day. The workflow reads stored Diandian credentials and cookies, exports data, sends reports to DingTalk, and leaves a debugging browser active. Scheduled execution is relevant to the declared daily-monitoring functionality and is not concealed. However, it materially expands the trust boundary: any later modification to scripts under the fixed project path will be executed automatically under the user's account at the next scheduled run. The documentation also describes OpenClaw Cron as a second scheduling mechanism, creating a risk of duplicate execution and duplicated data transmission if both are enabled. The launchd persistence is user-level rather than privileged sy ...[truncated 1514 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Dependencies use open-ended minimum versions without a reproducible lock file

Content
View full analysis
=0.25.0 apscheduler>=3.10.0 sqlalchemy>=2.0.0 pyyaml>=6.0 playwright>=1.40.0 structlog>=23.1.0 rich>=13.0.0 python-dotenv>=1.0.0 pytest>=7.4.0 pytest-asyncio>=0.21.0 ``` The documented installation command resolves these specifications directly: ```bash ./venv/bin/pip install -r requirements.txt ``` ### Technical Analysis Every dependency uses an open-ended `>=` constraint. Consequently, two installations at different times can receive different package versions, including future major versions and newly published transitive dependencies that were never reviewed with this Skill. This does not prove that any named dependency is currently malicious. The packages shown are established public packages and no typo-squatted name was identified. The confirmed issue is the absence of reproducible version resolution and integrity hashes for software that executes locally and, in Playwright's case, controls an authenticated browser. Because the project also documents persistent scheduled execution, a compromised or incompatible dependency installed into the Skill's virtual environment may subsequently execute unattended. ### Attack Path 1. A user follows the README and runs `pip install -r requirements.txt`. 2. Pip resolves the newest versions satisfying the open-ended minimum constraints. 3. A future compromised release, malicious transitive dependency, or incompatible major update is selected. 4. Package installation or later import executes affected code inside the Skill's Python environment. 5. During scheduled execution, the dependency can access the same files, stored credentials, browser session, reports, and network available to the Skill process. ### Impact Assessment A malicious dependenc ...[truncated 544 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (206)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

The README instructs users to run rm -rf /var/folders/.../playwright-artifacts-* as a cleanup step. Although the intent is troubleshooting, forceful recursive deletion commands are risky in agent or copy-paste workflows because path mistakes, shell expansion, or environmental differences can remove unintended files.

Content

Scanner excerpt · README.md (reported line 200)May include surrounding context.

  • ✅ 已自动修复,程序会自动重新下载(最多重试一次)
  • 如果仍然失败,可以清空下载目录重试:
    bash
    rm -rf /var/folders/hv/ky0s2_b96k1f69x2z0my_8nc0000gn/T/playwright-artifacts-*
    

2. 页面标题不匹配

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

The README instructs users to run rm -rf /var/folders/.../playwright-artifacts-* as a cleanup step. Although the intent is troubleshooting, forceful recursive deletion commands are risky in agent or copy-paste workflows because path mistakes, shell expansion, or environmental differences can remove unintended files.

Content

Scanner excerpt · README.md (reported line 200)May include surrounding context.

  • ✅ 已自动修复,程序会自动重新下载(最多重试一次)
  • 如果仍然失败,可以清空下载目录重试:
    bash
    rm -rf /var/folders/hv/ky0s2_b96k1f69x2z0my_8nc0000gn/T/playwright-artifacts-*
    

2. 页面标题不匹配

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

Referencing automatic login from config/credentials.yaml indicates the skill relies on stored reusable credentials for unattended access. In a task automation skill, this is more dangerous because the credentials are likely long-lived and repeatedly accessed by background jobs, making compromise of the workspace or logs materially more impactful.

Content

Scanner excerpt · docs/定时任务配置说明.md (reported line 196)May include surrounding context.

md
**现象**: 获取数据失败,提示未登录

**解决**: 脚本已集成自动登录功能,会自动使用 `config/credentials.yaml` 中的账号密码重新登录

#### 3. 浏览器未运行

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · skill.py (reported line 25)May include surrounding context.

python
if cwd is None:
        cwd = SRC_DIR
    
    env = {**os.environ, "PYTHONPATH": str(SRC_DIR)}
    
    try:
        result = subprocess.run(

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

This code path persists credentials to credentials.yaml, enabling long-term local storage of reusable secrets. In the context of an auto-login skill, the presence of a save_credentials helper increases risk because it normalizes secret persistence and may be invoked by future code or operators without safeguards.

Content

Scanner excerpt · tools/diandian_auto_login.py (reported line 35)May include surrounding context.

python
def save_credentials(username, password):
    """保存账号密码"""
    config_path = CONFIG_DIR / "credentials.yaml"
    config = {}
    
    if config_path.exists():

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · README.md (reported line 112)May include surrounding context.

md
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · README_DIANDIAN.md (reported line 70)May include surrounding context.

md
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · README_DIANDIAN.md (reported line 161)May include surrounding context.

md
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · README_DIANDIAN.md (reported line 183)May include surrounding context.

md
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_android_fetch.py (reported line 28)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_auto_login.py (reported line 25)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_auto_login.py (reported line 222)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_auto_login_v2.py (reported line 21)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_connect.py (reported line 537)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_connect.py (reported line 541)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_export_monitor.py (reported line 27)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_full_export.py (reported line 28)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_full_export_fixed.py (reported line 28)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_huawei_export.py (reported line 25)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_huawei_export.py (reported line 64)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · tools/diandian_huawei_full.py (reported line 36)May include surrounding context.

python
def load_credentials() -> dict:
    """加载账号凭证"""
    cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
    if not cred_path.exists():
        raise FileNotFoundError(f"凭证文件不存在:{cred_path}")

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code extracts the full browser cookie string and writes it to a YAML configuration file, effectively persisting an authenticated session token. If that file is read by another process, committed to source control, or accessed by an attacker, the token could be replayed to impersonate the user and access the account.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

This code accesses credentials from a local configuration file for automated authentication. In a skill environment, credential harvesting and use are especially sensitive because the skill may be run with access to secrets the user did not intend to expose to automation, and those credentials enable account access on an external service.

Content

Scanner excerpt · tools/diandian_connect.py (reported line 461)May include surrounding context.

python
print("   ❌ 检测到未登录状态,开始自动登录...")
                    
                    # 加载账号密码
                    credentials_path = CONFIG_DIR / "credentials.yaml"
                    if credentials_path.exists():
                        with open(credentials_path, 'r', encoding='utf-8') as f:
                            credentials = yaml.safe_load(f)

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

Referencing a local credentials.yaml file for account secrets is a credential-access pattern that enables the skill to retrieve and use stored authentication material. In this context, the danger is amplified because the credentials are immediately used to access a real external service, creating risk of unauthorized account use if the skill runs unexpectedly or is repurposed.

Content

Scanner excerpt · tools/diandian_huawei_correct.py (reported line 19)May include surrounding context.

python
REPORTS_DIR.mkdir(exist_ok=True)

def load_credentials():
    config_path = CONFIG_DIR / "credentials.yaml"
    if config_path.exists():
        with open(config_path, 'r', encoding='utf-8') as f:
            config = yaml.safe_load(f)

Static analysis

No suspicious patterns detected.