Back to skill

Security audit

Competitor Watch

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed competitor-monitoring scaffold that stores local snapshots and reports changes, with some implementation weaknesses users should understand before scheduling it.

Install only if you are comfortable with a shell-script scaffold that writes local monitoring data under ~/.config/competitor-watch and may be run repeatedly by cron. Review and trust the config.json contents before running checks, avoid running it from shared or attacker-writable directories, and prefer dry-run first. The current scripts do not appear malicious, but they should be hardened before unattended production 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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check.sh:72
Finding

Configuration-Controlled Path Traversal in Snapshot Storage

Content
View full analysis

Vulnerability Details

File Location: scripts/check.sh, lines 72–75 and 89–120
Vulnerability Type: Path traversal through unvalidated configuration values
Risk Level: Medium

Vulnerable Code

bash
comp_id=$(echo "$competitor_json" | jq -r '.id')
comp_name=$(echo "$competitor_json" | jq -r '.name')
comp_tier=$(echo "$competitor_json" | jq -r '.tier')
comp_pages=$(echo "$competitor_json" | jq -c '.pages')

echo "$comp_pages" | jq -c 'to_entries[]' | while IFS= read -r page_entry; do
    page_name=$(echo "$page_entry" | jq -r '.key')
    page_url=$(echo "$page_entry" | jq -r '.value')

    # ...

    comp_snapshot_dir="$SNAPSHOT_DIR/$comp_id/$page_name"
    mkdir -p "$comp_snapshot_dir"

    timestamp=$(date +"%Y-%m-%d-%H%M%S")
    new_snapshot_file="$comp_snapshot_dir/$timestamp.txt"

    echo "    - Saving snapshot to $new_snapshot_file"
    if [ "$DRY_RUN" = false ]; then
        echo "$new_content" > "$new_snapshot_file"
    fi

Technical Analysis

The competitor ID and page name are read from the editable JSON configuration and used directly as path components. The script does not validate them against an allowlist, reject path separators, canonicalize the resulting path, or verify that it remains under SNAPSHOT_DIR.

Shell quoting prevents these values from being split into commands, but it does not prevent filesystem traversal. A value containing components such as ../ can cause the operating system to resolve the snapshot path outside the intended data directory. An absolute page key may also undermine the intended directory layout, depending on how the resulting path is composed and normalized.

The normal add-competitor.sh workflow generates constrained competitor IDs, but config.json is explicitly intended to be edited by users and could also be replaced or generated by another process. Therefore, check.sh must not assume that configuration path components are sa ...[truncated 1385 chars]

Remediation
View remediation

Remediation Suggestions

  1. Validate every competitor ID and page key before using it as a path component. Use a strict allowlist such as ^[A-Za-z0-9_-]+$.
  2. Reject empty values, . and .., slash characters, backslashes, control characters, and absolute paths.
  3. Canonicalize both SNAPSHOT_DIR and the proposed destination with realpath or an equivalent mechanism.
  4. Verify that the canonical destination begins with the canonical snapshot directory followed by a path separator.
  5. Abort rather than silently processing an unsafe configuration entry.
  6. Apply restrictive directory permissions, such as mode 0700, to reduce access from other local users.
  7. Perform validation in check.sh even if add-competitor.sh also validates input, because the configuration can be edited independently.

Example validation:

bash
validate_component() {
    local value="$1"
    [[ "$value" =~ ^[A-Za-z0-9_-]+$ ]] || {
        log_error "Unsafe snapshot path component: $value"
    }
}

validate_component "$comp_id"
validate_component "$page_name"

snapshot_root=$(realpath -m "$SNAPSHOT_DIR")
comp_snapshot_dir=$(realpath -m "$SNAPSHOT_DIR/$comp_id/$page_name")

case "$comp_snapshot_dir/" in
    "$snapshot_root/"*) ;;
    *) log_error "Snapshot path escapes the configured data directory." ;;
esac

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check.sh:145
Finding

Predictable Temporary File Permits Symlink-Based File Clobbering

Content
View full analysis

Vulnerability Details

File Location: scripts/check.sh, lines 145 and 165
Vulnerability Type: Insecure predictable temporary file
Risk Level: Medium

Vulnerable Code

bash
# Log the change
if [ "$DRY_RUN" = false ]; then
    change_data=$(echo "$diff_output" | jq --arg id "$change_id" --arg comp "$comp_id" --arg page "$page_name" '. + {id: $id, competitor: $comp, page: $page, timestamp: now | todate}')
    jq --argjson data "$change_data" '.[$data.id] = $data' "$CHANGE_LOG_FILE" > tmp.json && mv tmp.json "$CHANGE_LOG_FILE"

    # --- Call Report Script ---
    log_info "    - Generating alert..."
    "$SKILL_DIR/scripts/report.sh" --alert "$change_id"
fi

# ...

# Update last check time
if [ "$DRY_RUN" = false ]; then
    jq --arg key "$check_key" --arg time "$current_timestamp" '.[$key] = ($time | tonumber)' "$LAST_CHECKS_FILE" > tmp.json && mv tmp.json "$LAST_CHECKS_FILE"
fi

Technical Analysis

Both state-file updates use the fixed relative filename tmp.json in the process's current working directory. This creates a time-of-check/time-of-use and symlink-following risk.

Shell output redirection opens tmp.json before jq executes. If another local process has pre-created tmp.json as a symbolic link, the shell follows that link and truncates the linked target. The subsequent mv does not prevent the initial truncation. Because the name is fixed and globally predictable within the working directory, an attacker does not need to win a race to guess the filename.

Reusing the same temporary path for different state files also creates collision and corruption risks when multiple monitoring sweeps execute concurrently.

Attack Path

  1. A local attacker identifies a directory from which check.sh is regularly executed and has permission to create entries there.
  2. The attacker creates tmp.json as a symbolic link to another file writable by the victim acco ...[truncated 1062 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the fixed tmp.json name with a unique file created by mktemp.
  2. Create temporary files inside the same trusted configuration directory as the destination. This supports atomic renaming while avoiding an attacker-controlled working directory.
  3. Register an EXIT trap to remove temporary files after failures or interruption.
  4. Use separate temporary files for separate state updates.
  5. Set a restrictive umask, such as 077, before creating configuration state.
  6. Prevent overlapping scheduled executions with flock or another locking mechanism.
  7. Validate the generated JSON before atomically renaming it over the destination.

Example hardened update:

bash
umask 077
temp_file=$(mktemp "$DATA_DIR/.last-checks.XXXXXX")
trap 'rm -f "$temp_file"' EXIT

jq --arg key "$check_key" \
   --arg time "$current_timestamp" \
   '.[$key] = ($time | tonumber)' \
   "$LAST_CHECKS_FILE" > "$temp_file"

jq empty "$temp_file"
mv -- "$temp_file" "$LAST_CHECKS_FILE"
trap - EXIT

Use a corresponding independently created temporary file for CHANGE_LOG_FILE, and lock the update sequence if concurrent execution is possible.

Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a full automated competitor-monitoring system with continuous tracking, change detection, pricing monitoring, and alerting. The supplied code chunk does not perform any of those core functions. Instead, it only manages configuration by adding a competitor record to a local JSON file. While this script could be a supporting setup component of a broader competitor-watch system, the behavior in this code chunk materially differs from the declared purpose if evaluated on its own, because the chunk lacks the headline capabilities described.

Content

No source excerpt is available for this finding.

YARA rule 'network_reconnaissance': Network reconnaissance and scanning patterns [hacktools]

Medium
Category
YARA Match
Confidence
65% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

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

md
-
name: competitor-watch
description: Know what your competitors ship before their customers do. Automated monitoring of competitor websites, product pages, pricing, content, and social presence. Detects changes, extracts new features, tracks pricing updates, and alerts you with digestible summaries. Your agent watches the competition 24/7 so you can focus on building. Configure competitor tiers (fierce rivals get deep monitoring, adjacents get high-level), set check frequency, define alert thresholds, and receive smart diffs highlighting what actually matters. Use when setting up competitive intelligence, tracking product launches, monitoring pricing changes, or staying ahead of market moves.
metadata:
  clawdbot:
    emoji: "🔍"
    requires:
      skills: []
---

# Competitor Watch — Your 24/7 Competitive Intelligence Agent

**They ship a feature. You know in minutes, not weeks.**

Competitor Watch monitors your competitive landscape automatically—websites, product pages, pric

YARA rule 'network_reconnaissance': Network reconnaissance and scanning patterns [hacktools]

Medium
Category
YARA Match
Confidence
65% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · config.example.json (reported line 7)May include surrounding context.

json
{
  "competitors": [
    {
      "id": "acme-corp",
      "name": "Acme Corp",
      "homepage": "https://acme.com",
      "tier": "fierce",
      "tags": ["direct-competitor", "ai-tools", "enterprise"],
      "pages": {
        "pricing": "https://acme.com/pricing",
        "features": "https://acme.com/features",
        "docs": "https://acme.com/docs",
        "blog": "https://acme.com/blog",
        "changelog": "https://acme.com/changelog"
      },
      "social": {
        "twitter": "@acmecorp",
        "linkedin": "company/acme-corp"
      },
      "notes": "Our primary competitor in mid-market. Watch for pricing and feature parity."
    },
    {
      "id": "competitor-b",
      "name": "Competitor B",
      "home

YARA rule 'network_reconnaissance': Network reconnaissance and scanning patterns [hacktools]

Medium
Category
YARA Match
Confidence
65% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · scripts/add-competitor.sh (reported line 18)May include surrounding context.

sh
{
  "competitors": [
    {
      "id": "acme-corp",
      "name": "Acme Corp",
      "homepage": "https://acme.com",
      "tier": "fierce",
      "tags": ["direct-competitor", "ai-tools", "enterprise"],
      "pages": {
        "pricing": "https://acme.com/pricing",
        "features": "https://acme.com/features",
        "docs": "https://acme.com/docs",
        "blog": "https://acme.com/blog",
        "changelog": "https://acme.com/changelog"
      },
      "social": {
        "twitter": "@acmecorp",
        "linkedin": "company/acme-corp"
      },
      "notes": "Our primary competitor in mid-market. Watch for pricing and feature parity."
    },
    {
      "id": "competitor-b",
      "name": "Competitor B",
      "home

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/diff.sh (reported line 62)May include surrounding context.

sh
fi

# --- Scoring ---
# This is a simplistic scoring model. It could be expanded significantly.
# We'll use the percentage change as a base score.
SCORE=$CHANGE_PERCENT

YARA rule 'network_reconnaissance': Network reconnaissance and scanning patterns [hacktools]

Medium
Category
YARA Match
Confidence
65% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · scripts/setup.sh (reported line 46)May include surrounding context.

sh
y data file: $file"
  else
    echo "• Data file $file already exists (skipped creation)"
  fi
done

echo ""
echo "🎉 Setup complete!"
echo ""
echo "Next steps:"
echo "  1. Edit $CONFIG_DIR/config.json to define your competitors and tiers."
echo "  2. Use the 'add-competitor.sh' script to easily add new companies."
echo "     → scripts/add-competitor.sh \"Acme Corp\" https://acme.com --tier fierce"
echo "  3. Run your first check (in dry-run mode to test):"
echo "     → scripts/check.sh --dry-run"
echo ""
echo "🎯 Your agent is now ready to watch the competition."

Static analysis

No suspicious patterns detected.