# Hacker Bob Security Evaluator (`hacker-bob/hacker-bob-agent`) Actor

Authorized-scope autonomous web-app security evaluator (recon, HTTP, headless-browser only) driving the unmodified Hacker Bob MCP engine. Ships a self-contained loopback demo that runs out of the box. Offensive Docker tooling stays dormant; model access is keyless via Apify's OpenRouter proxy.

- **URL**: https://apify.com/hacker-bob/hacker-bob-agent.md
- **Developed by:** [Hacker Bob](https://apify.com/hacker-bob) (community)
- **Categories:** Agents, Developer tools
- **Stats:** 2 total users, 1 monthly users, 6.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $500,000.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Hacker Bob — Authorized Web-App Security Evaluator

<img src="https://raw.githubusercontent.com/vmihalis/hacker-bob/main/docs/hacker-bob.png" alt="Hacker Bob" width="200">

**Point it at your own running app, live API, or PR surface** → an autonomous **recon → HTTP → headless-browser** security evaluation, sealed into one signed report.

#### 🌐 Website: **[hackerbob.ai](https://hackerbob.ai)**

The official Hacker Bob engine, packaged for Apify.

[![$500 per result](https://img.shields.io/badge/%24500-per%20result-eab308?style=for-the-badge&labelColor=0b1220)](https://hackerbob.ai) [![web-only](https://img.shields.io/badge/scope-web--only-06b6d4?style=for-the-badge&labelColor=0b1220)](https://hackerbob.ai) [![hackerbob.ai](https://img.shields.io/badge/hackerbob.ai-e5e7eb?style=for-the-badge&labelColor=0b1220)](https://hackerbob.ai)

---

> ## 💵 $500 per result
> One signed report per completed run — **$500 per completed evaluation**. A run that delivers no report (rejected input, or a failure before the report) writes no result and is **not** charged.

---

### What it does

Give it a target you're authorized to test; it maps the live surface, probes HTTP, drives a headless browser, verifies findings, grades, and delivers a signed report — fully autonomous, over the official Hacker Bob MCP engine.

- 🎯 **Your target** — a running app, live API, or PR/review URL. Not a repo to clone.
- 🔏 **One signed result** — `report.md` + evidence to the key-value store, one structured dataset row (grade, severity, top finding).
- 🧱 **Web-only** — repo-review and smart-contract/RPC tooling is removed at the tool layer; it can only reach the live surface you point it at.

### Quick start

1. Set **Target URL** to a web app / API / PR you own or may test.
2. Tick **I am authorized to security-test this target**.
3. **Start** (defaults are fine).

No target? Enable **Try the built-in demo instead** for a self-contained sample run (bills the same $500).

### Input

| Field | Default | Meaning |
|---|---|---|
| `target` | *(empty)* | Your authorized web app / API / PR URL. |
| `confirmAuthorizedTarget` | `false` | Attestation — **required** for any non-loopback target. |
| `useDemoTarget` | `false` | Run the built-in loopback demo instead. |
| `modelProvider` | `apify-openrouter` | Inference route: keyless (default), or BYO `anthropic` / `openrouter` key. |
| `modelApiKey` | *(empty)* | Your key — only for `anthropic` / `openrouter`. Secret, never logged. |
| `budget` | `2` | Max model spend, USD. Clamped `[0, 20]`. |
| `timeout` | `780` | Invocation timeout (s), clamped `[1, 43200]`. Bob's pipeline runs for hours; latest snapshot is salvaged on timeout. |
| `mode` | `normal` | `paranoid` adds private-address egress blocking. |

### Scope & authorization

**Authorization is your responsibility** — Bob probes any target you give him and does **not** verify permission. Only run against a system you own, an active bug-bounty scope, or a written pentest agreement. A non-demo target without `confirmAuthorizedTarget` is refused before any spend, and the per-run domain pin prevents a prompt-injected model from pivoting to another host. Unauthorized access is a crime in most jurisdictions (US CFAA, EU 2013/40/EU, UK CMA 1990).

### Provenance

The official [`hackerbob.ai`](https://hackerbob.ai) engine ([`vmihalis/hacker-bob`](https://github.com/vmihalis/hacker-bob), Apache-2.0), vendored byte-identical except a per-run target pin and a `BOB_APIFY_WEB_ONLY` tool-family filter. No offensive capability added — only removed or infra-starved.

---

*Official Hacker Bob — [hackerbob.ai](https://hackerbob.ai) · authorized testing only · $500 per result*

# Actor input Schema

## `target` (type: `string`):

A URL you are authorized to test: your own running web app, live API, or a PR/code-review URL — not a repository to clone. Hacker Bob evaluates the live surface only (HTTP + headless browser). In this deployment the repo-review and smart-contract/RPC tool families are disabled at the tool layer (BOB_APIFY_WEB_ONLY), so the running session has no path to your source code or an RPC endpoint (see "I am authorized..." below). Leave this empty and enable "Try the built-in demo instead" to run against a sample target instead of your own.
## `confirmAuthorizedTarget` (type: `boolean`):

I attest that I own this live target (running app / API / PR surface) or have explicit written authorization to security-test it. Required for any target other than a loopback address — the scope gate fails closed (refuses the run) on any other target left unattested. Not needed when "Try the built-in demo instead" is enabled.
## `useDemoTarget` (type: `boolean`):

Skip pointing this at your own target and run against the Actor's built-in loopback demo fixture instead — a live, self-contained fixture, not a repository — so you can see a sample report with zero setup. Off by default — this Actor is meant to be run against your own authorized live target.
## `modelProvider` (type: `string`):

Where model inference for this run is routed and billed. "Keyless" needs no setup and is billed as ordinary Apify platform usage on your Apify account — but on Apify's FREE plan, Apify's own keyless proxy caps each response at 2048 tokens (partial runs). That cap is asserted by Apify's platform, not verified or enforced by this Actor's own code. Use a paid Apify plan or bring your own key for full runs. The other two options use your own API key (see "Model API key" below) and are billed directly by that provider instead.
## `modelApiKey` (type: `string`):

Your own API key or token, used ONLY when "Model provider" above is set to Anthropic or OpenRouter — ignored (and not needed) for the default keyless option. For Anthropic this accepts either a standard API key or a Claude Code OAuth token (from `claude setup-token`, starting with sk-ant-oat). Stored as a secret input; never logged. Security note: this key is reachable by the autonomous process while it reads and acts on content served by the target you're testing — use a scoped, rotatable key you can revoke independently of your other usage, not a broadly-privileged primary key.
## `model` (type: `string`):

Optional model slug override, if you want a specific model instead of the provider's default (e.g. a specific Claude version). Leave blank to use the default for the selected model provider.
## `budget` (type: `number`):

Maximum model spend for this run, in US dollars, enforced as a hard cap on the invocation. A higher budget lets the evaluator dig deeper — more surface coverage, more finding verification — before it has to wrap up; a low budget produces a faster but shallower report. Set budget to 0 to run UNCAPPED (run to completion) — billed to your own inference budget; positive values are a hard cap up to $20.
## `timeout` (type: `integer`):

Wall-clock limit for the run, in seconds (max 43200 = 12h). Bob's full evaluation pipeline is a MANY-HOUR process and only emits a report after it completes, so a deep run needs a large budget. The invocation is terminated at this deadline and the latest report snapshot produced so far is salvaged and written. If you raise this, also raise the Actor's platform run timeout (Console/API) to match, or the container is stopped first.
## `mode` (type: `string`):

How aggressively Hacker Bob probes your live target. "Normal" is the default balance of coverage and safety; "Paranoid" adds direct-egress private-address blocking (extra guardrails against SSRF/DNS-rebinding while testing a live surface); "YOLO" is the most permissive, with fewer guardrails on live probing.

## Actor input object example

```json
{
  "confirmAuthorizedTarget": false,
  "useDemoTarget": false,
  "modelProvider": "apify-openrouter",
  "budget": 2,
  "timeout": 780,
  "mode": "normal"
}
````

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("hacker-bob/hacker-bob-agent").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("hacker-bob/hacker-bob-agent").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call hacker-bob/hacker-bob-agent --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=hacker-bob/hacker-bob-agent",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Hacker Bob Security Evaluator",
        "description": "Authorized-scope autonomous web-app security evaluator (recon, HTTP, headless-browser only) driving the unmodified Hacker Bob MCP engine. Ships a self-contained loopback demo that runs out of the box. Offensive Docker tooling stays dormant; model access is keyless via Apify's OpenRouter proxy.",
        "version": "0.1",
        "x-build-id": "71rJ5d6Hc8X8JawkK"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/hacker-bob~hacker-bob-agent/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-hacker-bob-hacker-bob-agent",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/hacker-bob~hacker-bob-agent/runs": {
            "post": {
                "operationId": "runs-sync-hacker-bob-hacker-bob-agent",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/hacker-bob~hacker-bob-agent/run-sync": {
            "post": {
                "operationId": "run-sync-hacker-bob-hacker-bob-agent",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "target": {
                        "title": "Target URL",
                        "type": "string",
                        "description": "A URL you are authorized to test: your own running web app, live API, or a PR/code-review URL — not a repository to clone. Hacker Bob evaluates the live surface only (HTTP + headless browser). In this deployment the repo-review and smart-contract/RPC tool families are disabled at the tool layer (BOB_APIFY_WEB_ONLY), so the running session has no path to your source code or an RPC endpoint (see \"I am authorized...\" below). Leave this empty and enable \"Try the built-in demo instead\" to run against a sample target instead of your own."
                    },
                    "confirmAuthorizedTarget": {
                        "title": "I am authorized to security-test this target",
                        "type": "boolean",
                        "description": "I attest that I own this live target (running app / API / PR surface) or have explicit written authorization to security-test it. Required for any target other than a loopback address — the scope gate fails closed (refuses the run) on any other target left unattested. Not needed when \"Try the built-in demo instead\" is enabled.",
                        "default": false
                    },
                    "useDemoTarget": {
                        "title": "Try the built-in demo instead",
                        "type": "boolean",
                        "description": "Skip pointing this at your own target and run against the Actor's built-in loopback demo fixture instead — a live, self-contained fixture, not a repository — so you can see a sample report with zero setup. Off by default — this Actor is meant to be run against your own authorized live target.",
                        "default": false
                    },
                    "modelProvider": {
                        "title": "Model provider",
                        "enum": [
                            "apify-openrouter",
                            "anthropic",
                            "openrouter"
                        ],
                        "type": "string",
                        "description": "Where model inference for this run is routed and billed. \"Keyless\" needs no setup and is billed as ordinary Apify platform usage on your Apify account — but on Apify's FREE plan, Apify's own keyless proxy caps each response at 2048 tokens (partial runs). That cap is asserted by Apify's platform, not verified or enforced by this Actor's own code. Use a paid Apify plan or bring your own key for full runs. The other two options use your own API key (see \"Model API key\" below) and are billed directly by that provider instead.",
                        "default": "apify-openrouter"
                    },
                    "modelApiKey": {
                        "title": "Model API key",
                        "type": "string",
                        "description": "Your own API key or token, used ONLY when \"Model provider\" above is set to Anthropic or OpenRouter — ignored (and not needed) for the default keyless option. For Anthropic this accepts either a standard API key or a Claude Code OAuth token (from `claude setup-token`, starting with sk-ant-oat). Stored as a secret input; never logged. Security note: this key is reachable by the autonomous process while it reads and acts on content served by the target you're testing — use a scoped, rotatable key you can revoke independently of your other usage, not a broadly-privileged primary key."
                    },
                    "model": {
                        "title": "Model override",
                        "type": "string",
                        "description": "Optional model slug override, if you want a specific model instead of the provider's default (e.g. a specific Claude version). Leave blank to use the default for the selected model provider."
                    },
                    "budget": {
                        "title": "Budget (USD)",
                        "minimum": 0,
                        "maximum": 20,
                        "type": "number",
                        "description": "Maximum model spend for this run, in US dollars, enforced as a hard cap on the invocation. A higher budget lets the evaluator dig deeper — more surface coverage, more finding verification — before it has to wrap up; a low budget produces a faster but shallower report. Set budget to 0 to run UNCAPPED (run to completion) — billed to your own inference budget; positive values are a hard cap up to $20.",
                        "default": 2
                    },
                    "timeout": {
                        "title": "Timeout (seconds)",
                        "minimum": 1,
                        "maximum": 43200,
                        "type": "integer",
                        "description": "Wall-clock limit for the run, in seconds (max 43200 = 12h). Bob's full evaluation pipeline is a MANY-HOUR process and only emits a report after it completes, so a deep run needs a large budget. The invocation is terminated at this deadline and the latest report snapshot produced so far is salvaged and written. If you raise this, also raise the Actor's platform run timeout (Console/API) to match, or the container is stopped first.",
                        "default": 780
                    },
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "normal",
                            "paranoid",
                            "yolo"
                        ],
                        "type": "string",
                        "description": "How aggressively Hacker Bob probes your live target. \"Normal\" is the default balance of coverage and safety; \"Paranoid\" adds direct-egress private-address blocking (extra guardrails against SSRF/DNS-rebinding while testing a live surface); \"YOLO\" is the most permissive, with fewer guardrails on live probing.",
                        "default": "normal"
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
