Skip to main content
skill CX Codex

profile-website-bot-detection — Claude Code skill: how to install

Profiles target sites for bot detection vendors using Kernel browsers in stealth and normal modes. Helpful when diagnosing scraping blocks and challenge types.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add kernel/skills --skill profile-website-bot-detection -a claude-code
Open on GitHub

What profile-website-bot-detection does

This skill inspects target web pages to uncover anti-bot providers, specific defense products, and active challenge screens. It provisions two browser sessions through the Kernel CLI: one standard instance and one stealth instance.

Both sessions navigate to the target address, write CDP analysis reports, and structure the output into JSON files. To isolate browser fingerprinting from network reputation, the stealth session disables its default proxy with --disable-default-proxy. The scripts exit immediately if KERNEL_API_KEY, Bash, jq, or Node.js 22+ are missing from your environment.

What it helps with

  • identify anti-bot vendors and protection products on a website
  • compare blocking behavior between normal and stealth browser sessions
  • detect active verification challenges before scraping
  • isolate fingerprint mitigation from proxy routing during bot tests

Example prompts for the agent

  • Profile https://example.com to identify active bot detection vendors using Kernel.
  • Run a comparative stealth analysis against https://example.com.
  • Inspect bot challenge types for the site in TARGET_URL.

How to install profile-website-bot-detection

1Skills CLI (Vercel)

npx skills add kernel/skills --skill profile-website-bot-detection -a claude-code

2Manually: put the files in place

~/.claude/skills/profile-website-bot-detection/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/profile-website-bot-detection/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about profile-website-bot-detection

How to install profile-website-bot-detection in Claude Code?

Run npx skills add kernel/skills --skill profile-website-bot-detection -a claude-code in your terminal.

What does profile-website-bot-detection do?

It visits a site using normal and stealth Kernel browsers, identifies bot detection vendors, and compares block verdicts.

What dependencies are needed before running the analysis?

You need Bash, jq, Node.js 22+, authenticated Kernel CLI, and the KERNEL_API_KEY environment variable.

Source text

SKILL.md · MIT
---
name: profile-website-bot-detection
description: Profile a website for bot detection vendors using stealth vs non-stealth Kernel browsers. Use when analyzing bot detection on a website, comparing stealth effectiveness, identifying anti-bot vendors and products, or detecting challenge types.
---

# Profile Website for Bot Detection Vendors

Analyzes a target website to identify bot detection vendors, their specific products, and challenge types. Supports comparative analysis between stealth and non-stealth browser modes.

## Prerequisites

- Kernel CLI installed and authenticated
- Bash, Node.js 22+, and `jq` installed
- `KERNEL_API_KEY` exported for the analysis script; never print it or ask the user to paste it into chat

## Comparative Workflow (Recommended)

Compare bot detection behavior between stealth and non-stealth browsers while controlling the network path. Run Steps 1-7 in the same Bash process so the cleanup trap remains armed. Do not run Step 2 as a standalone non-interactive shell: its EXIT trap will delete the sessions when that shell returns.

### Step 1: Prepare the Analyzer

Install dependencies before creating billable browser sessions:

```bash
cd scripts
npm install  # first run only
export TARGET_URL='https://example.com'
test -n "${KERNEL_API_KEY:-}" || { echo "KERNEL_API_KEY is not set" >&2; exit 1; }
```

### Step 2: Create Both Browser Types and Arm Cleanup

Use JSON output to capture exact session IDs. The EXIT trap cleans up the first session if the second creation or a later analysis fails.

```bash
set -euo pipefail
STEALTH_ID=
NORMAL_ID=

cleanup() {
  local ids=()
  [[ -n "${STEALTH_ID:-}" ]] && ids+=("$STEALTH_ID")
  [[ -n "${NORMAL_ID:-}" ]] && ids+=("$NORMAL_ID")
  if ((${#ids[@]})); then
    kernel browsers delete "${ids[@]}" || true
  fi
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

STEALTH_ID=$(kernel browsers create --stealth --viewport 1920x1080@25 --timeout 300 --output json | jq -er '.session_id')
NORMAL_ID=$(kernel browsers create --viewport 1920x1080@25 --timeout 300 --output json | jq -er '.session_id')

# Isolate browser stealth behavior: route the stealth session directly, like the normal session.
kernel browsers update "$STEALTH_ID" --disable-default-proxy >/dev/null
```

A stealth session otherwise uses Kernel's default stealth proxy, while a normal session connects directly. Without the update, the result measures **stealth plus proxy** versus **normal plus direct**, not stealth alone. Apply `--disable-default-proxy` only to the stealth session; the API rejects it for non-stealth sessions.

Choose one network design before navigating:

- **Browser-mode comparison (recommended):** Keep the update above so both sessions connect directly.
- **Platform-default comparison:** Omit the update and report the stealth result as `stealth + default proxy`; do not attribute differences solely to stealth.
- **Explicit-proxy comparison:** Optionally validate a configured proxy with `kernel proxies check "$PROXY_ID" --url https://example.com --output json`, add `--proxy-id "$PROXY_ID"` to both create commands, and omit the update. This controls proxy configuration, though provider rotation can still produce different exit IPs.

### Step 3: Run Analysis on Both Browsers

```bash
KERNEL_BROWSER_ID="$STEALTH_ID" TARGET_URL="$TARGET_URL" BROWSER_MODE=stealth npm run analyze
KERNEL_BROWSER_ID="$NORMAL_ID" TARGET_URL="$TARGET_URL" BROWSER_MODE=normal npm run analyze
```

Each analyzer run disconnects its local CDP client after writing the report; it leaves the Kernel session active for comparison and cleanup.

### Step 4: Compare Results

Compare the newest report from each mode rather than concatenating multiple JSON documents:

```bash
# Derive the same hostname folder used by the analyzer (e.g., chase.com -> chase-com).
HOST=$(node -e 'const host = new URL(process.env.TARGET_URL).hostname; console.log(host.replace(/^www\./, "").replace(/\./g, "-"))')
latest_report() {
  local reports=("$1"/report-*.json)
  [[ -e "${reports[0]}" ]] || return 1
  printf '%s\n' "${reports[@]}" | sort | tail -n 1
}
STEALTH_REPORT=$(latest_report "output/$HOST/stealth")
NORMAL_REPORT=$(latest_report "output/$HOST/normal")

echo "=== STEALTH VERDICT ===" && jq '.summary.verdict' "$STEALTH_REPORT"
echo "=== NORMAL VERDICT ===" && jq '.summary.verdict' "$NORMAL_REPORT"

echo "=== STEALTH BLOCKED ===" && jq '.summary | {isBlocked, blockedPages, blockedVendors}' "$STEALTH_REPORT"
echo "=== NORMAL BLOCKED ===" && jq '.summary | {isBlocked, blockedPages, blockedVendors}' "$NORMAL_REPORT"

echo "=== STEALTH VENDORS ===" && jq '.summary.vendorNames' "$STEALTH_REPORT"
echo "=== NORMAL VENDORS ===" && jq '.summary.vendorNames' "$NORMAL_REPORT"
```

### Step 5: Interpret Comparison

| Scenario | Stealth | Normal | Meaning |
|----------|---------|--------|---------|
| No vendors detected | 0 | 0 | No signals observed; do not assume the site has no bot detection |
| Same vendors, no blocks | N | N | Bot detection present, both pass |
| Normal blocked, stealth passes | 0 blocks | Blocked | With a controlled network, stealth is effective |
| Both blocked | Blocked | Blocked | Both tested configurations are blocked |
| Different challenge types | Lighter | Harder | With a controlled network, stealth likely reduces suspicion |

### Step 6: Provide Summary

After running the comparative analysis, state the network design explicitly. Claim stealth effectiveness only when both sessions used the same network class.

**Summary Report Template:**

```
## Bot Detection Comparative Analysis: [TARGET_URL]

### Verdict
- **Network Design**: [both direct / same explicit proxy config / platform defaults]
- **Stealth Browser**: [verdict from summary.verdict]
- **Normal Browser**: [verdict from summary.verdict]
- **Stealth Effectiveness**: [Effective/Ineffective/Inconclusive; use Inconclusive for mixed network paths]

### Block Status
| Browser | Blocked | Block Type | Evidence |
|---------|---------|------------|----------|
| Stealth | [Yes/No] | [blockType or N/A] | [first evidence item] |
| Normal  | [Yes/No] | [blockType or N/A] | [first evidence item] |

### Detected Vendors
| Vendor | Stealth | Normal | Products |
|--------|---------|--------|----------|
| [vendor] | ✓/✗ | ✓/✗ | [product list] |

### Analysis
- [Explain what the results mean]
- [Note any differences between stealth and normal]
- [Recommend next steps if blocked]

### Key Findings
1. [Finding 1]
2. [Finding 2]
3. [Finding 3]
```

Use the JSON reports to populate this template:
- `summary.verdict` - The final verdict string
- `summary.isBlocked` - Whether the browser was blocked
- `summary.blockedPages` - Details about blocked pages
- `summary.vendorNames` - List of detected vendors
- `vendorDetections` - Detailed vendor/product information

### Step 7: Cleanup

Delete both sessions immediately after collecting reports. `browsers delete` is non-interactive and accepts multiple IDs; it has no `-y` flag.

```bash
cleanup
trap - EXIT INT TERM
```

If the shell was interrupted or the variables were lost, use `kernel browsers list` to find the created sessions and delete each with `kernel browsers delete <session-id>`.

---

## Interpreting Results

The analysis detects vendors and their specific products:

| Vendor | Products Detected |
|--------|-------------------|
| **Akamai** | Bot Manager, Bot Manager Premier, mPulse RUM, Sensor Script, Edge DNS |
| **Cloudflare** | Bot Management, Turnstile, Challenge Platform, JS Challenge, Managed Challenge |
| **DataDome** | Interstitial Challenge, Slider Challenge, Device Check, Picasso Fingerprint |
| **HUMAN/PerimeterX** | Bot Defender, Sensor SDK, Press & Hold Challenge |
| **Imperva/Incapsula** | Advanced Bot Protection (utmvc), Advanced Bot Protection (reese84), WAF |
| **Kasada** | IPS (Initial Page Security), FP (Fingerprint Endpoint), Telemetry, POW Challenge |
| **Google** | reCAPTCHA v2, reCAPTCHA v3, reCAPTCHA Enterprise |
| **hCaptcha** | Widget, Enterprise |
| **FingerprintJS** | Fingerprint Pro, BotD |
| **Arkose Labs** | FunCaptcha |

Detection methods:
- URL pattern matching for vendor scripts and endpoints
- Cookie analysis (e.g., `_abck`, `__cf_bm`, `datadome`, `_px*`)
- Header detection (e.g., `cf-ray`, `x-kpsdk-*`, `x-d-token`)
- Challenge detection from response status codes

Vendor-specific checks:
- **DataDome**: Hard IP block detection (`dd.t === 'bv'`)
- **Akamai**: Cookie validity check (`~0~` indicator)
- **Kasada**: Flow type detection (IPS vs FP)

### Pages Analyzed

The script automatically analyzes:
1. **Homepage** - Initial page load and bot detection scripts
2. **Login page** - Automatically discovered via link detection or common paths (`/login`, `/signin`, etc.)

Login pages often have more aggressive bot detection due to credential stuffing prevention.

### Output Files

Results are organized by target hostname in `scripts/output/<hostname>/<mode>/`:
- `report-<timestamp>.json` - Full JSON report with vendor detections
- `screenshot-homepage-<timestamp>.png` - Homepage screenshot
- `screenshot-login-<timestamp>.png` - Login page screenshot (if found)

Example structure for comparative test on chase.com:
```
output/chase-com/
├── stealth/
│   ├── report-*.json
│   ├── screenshot-homepage-*.png
│   └── screenshot-login-*.png
└── normal/
    ├── report-*.json
    ├── screenshot-homepage-*.png
    └── screenshot-login-*.png
```

The JSON report includes:
- `summary`: Quick access to verdict, block status, and vendor names
  - `verdict`: Human-readable result (e.g., "BLOCKED - homepage (Error Page)")
  - `isBlocked`: Boolean - true if any page was blocked
  - `vendorNames`: Array of detected vendor names
  - `blockedPages`: Details of blocked pages with evidence
- `vendorDetections`: Map of detected vendors with products, URLs, cookies, headers
- `blockDetections`: Detailed block analysis for each page
- `vendorScriptsDetected`: URLs of detected vendor scripts (not saved to disk)
- `networkRequests/networkResponses`: All requests with vendor matching
- `cookies`: All cookies with vendor attribution

## Vendor-Specific Detection Notes

### Akamai
- Cookies: `_abck` (core validation), `bm_sz`, `bm_sv`
- Cookie `~0~` in `_abck` value = valid session

### Cloudflare
- Cookies: `__cf_bm`, `cf_clearance`
- Challenge: `/cdn-cgi/challenge-platform/`
- Turnstile: `challenges.cloudflare.com/turnstile`

### DataDome
- Cookie: `datadome`
- `dd.t === 'bv'` = hard IP block (changing IP required, solving captcha won't help)

### HUMAN/PerimeterX
- Cookies: `_px2`, `_px3`, `_pxhd`
- Press & Hold challenge requires behavioral simulation

### Imperva/Incapsula
- **utmvc**: Script via `/_Incapsula_Resource`
- **reese84**: Cookie or `x-d-token` header

### Kasada
- Headers: `x-kpsdk-ct`, `x-kpsdk-cd`
- Flow 1 (IPS): 429 on initial page load, must solve `ips.js` first
- Flow 2 (FP): Background `/fp` fingerprint requests

© kernel · MIT · kernel/skills

Similar skills

All skills for Browser and scraping →

From the same set6

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor