---
title: "Active Scanning Scripts"
description: "Create custom active scan rules for HawkScan - vulnerability detection, payload fuzzing, business logic testing, and injection patterns."
url: https://docs.stackhawk.com/scripting/script-types/active-scripting/
---

# Active Scanning Scripts

Create custom active scan rules for HawkScan - vulnerability detection, payload fuzzing, business logic testing, and injection patterns.

## **Usage Overview**

Active scripting in HawkScan enables developers to create custom vulnerability detection rules that actively test application endpoints. Unlike passive scanning which analyzes existing traffic, active scripts generate and send crafted requests to probe for security vulnerabilities, business logic flaws, or application-specific security issues not covered by HawkScan’s built-in rules.

Active scripts provide the ability to:

- Create custom parameter fuzzing logic with dynamic payload generation
- Test for business-specific vulnerabilities (e.g., multi-tenancy violations, privilege escalation)
- Implement specialized injection testing patterns tailored to the application
- Detect application-specific error conditions that indicate security weaknesses
- Build custom security checks for unique application behaviors

**Key Capabilities:**

- Send multiple crafted requests per endpoint parameter
- Modify request parameters, headers, and bodies programmatically
- Analyze responses for vulnerability indicators
- Raise alerts with custom risk levels, descriptions, and remediation guidance
- Integrate with HawkScan’s scanning workflow and alerting system

### **When to Use Active Scripts vs Built-in Rules**

HawkScan includes comprehensive built-in active scanning rules covering OWASP Top 10 vulnerabilities like SQL injection, XSS, command injection, and more. Active scripts become valuable when testing requires:

**Use Active Scripts When:**

- Testing for business logic vulnerabilities specific to the target application
- Implementing custom fuzzing strategies for unique parameter handling
- Checking multi-tenancy or authorization boundary violations
- Testing application-specific error handling behaviors
- Validating custom security controls or data validation logic
- Creating domain-specific vulnerability checks (e.g., financial calculations, access control)
- Generating test data dynamically based on application context

**Use Built-in Active Rules When:**

- Testing for common injection vulnerabilities (SQL, XSS, XXE, command injection)
- Checking for security misconfigurations (CORS, CSP, HSTS)
- Scanning for path traversal, SSRF, or deserialization vulnerabilities
- Detecting common framework-specific vulnerabilities
- Standard OWASP Top 10 coverage is sufficient

### **Usage Scenarios**

#### 1. **Parameter Fuzzing with Dynamic Payload Generation**

One of the most common uses for active scripts is intelligent parameter fuzzing—sending a variety of crafted values to parameters to detect unhandled input scenarios that cause server errors or unexpected behavior.

**Example:** Testing how an application handles unexpectedly long strings, special characters, or type mismatches:

- Generate random strings of varying lengths (1-100 characters)
- Test with special data patterns (Unicode, control characters, SQL fragments)
- Monitor for 5xx errors, excessive response times, or error messages in responses

**Use Case:** An API endpoint accepts a `name` parameter. The fuzzer sends 10 different values including very long strings and special characters. If the server returns a 500 error with a stack trace, a vulnerability has been found.

**Reference:** [fuzzer.kts example](https://github.com/kaakaww/javaspringvulny/blob/main/hawkscripts/active/fuzzer.kts)

#### 2. **Multi-Tenancy and Authorization Boundary Testing**

Business logic vulnerabilities often involve authorization checks. Active scripts can test whether users can access data belonging to other tenants or users.

**Example:** Testing if changing a user ID in a request allows access to another user’s data:

- Original request: `GET /api/users/user123`
- Modified request: `GET /api/users/user456`
- Check if response contains data from user456 when authenticated as user123

**Use Case:** A multi-tenant SaaS application should enforce tenant boundaries. The script modifies user/tenant IDs in requests and checks if unauthorized data is returned.

**Reference:** [tenancy-check.kts example](https://github.com/kaakaww/hawkscan-examples/blob/main/scripts/examples/active/tenancy-check.kts)

#### 3. **Custom Injection Pattern Testing**

While HawkScan includes SQL injection detection, testing may require application-specific injection vulnerabilities or custom payloads tuned to the target technology stack.

**Example:** Testing for NoSQL injection in MongoDB-backed APIs:

- Inject MongoDB query operators: `{"$gt": ""}`, `{"$ne": null}`
- Test JSON payload manipulation: `{"username": {"$regex": ".*"}}`
- Monitor for authentication bypass or data disclosure

**Use Case:** The API uses MongoDB and accepts JSON query parameters. Standard SQL injection tests won’t find NoSQL-specific issues. A custom active script tests MongoDB operator injection.

### **Simple Script/Template Example and Description**

Consider the following basic active scanning template, `active-template.kts`:

```kotlin
import org.apache.log4j.LogManager
import org.parosproxy.paros.network.HttpMessage
import com.stackhawk.hste.extension.scripts.scanrules.ScriptsActiveScanner

val logger = LogManager.getLogger("active-template")

// Entry point for node-level scanning (tests entire endpoints)
fun scanNode(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage) {
    logger.debug("scanNode: ${origMessage.requestHeader.uri}")

    // Example: Test the entire endpoint without parameter modification
    // val msg = origMessage.cloneRequest()
    // activeScanner.sendAndReceive(msg, false, false)
    // Analyze msg.responseHeader and msg.responseBody
}

// Entry point for parameter-level scanning (tests individual parameters)
fun scan(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage, param: String, value: String) {
    logger.debug("scan: ${origMessage.requestHeader.uri} | param=$param, value=$value")

    // Example: Clone the original request
    // val msg = origMessage.cloneRequest()

    // Example: Modify the parameter value
    // activeScanner.setParam(msg, param, "malicious_value")

    // Example: Send the modified request
    // activeScanner.sendAndReceive(msg, false, false)

    // Example: Check response for vulnerability indicators
    // if (msg.responseHeader.statusCode == 500) {
    //     raiseAlert(activeScanner, msg, "Server error detected", param, "malicious_value")
    // }
}

// Helper function to raise alerts (optional but recommended)
fun raiseAlert(activeScanner: ScriptsActiveScanner, msg: HttpMessage, evidence: String, param: String, attackValue: String) {
    val risk = 2 // 0: info, 1: low, 2: medium, 3: high
    val confidence = 3 // 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed
    val title = "Custom Vulnerability Detected"
    val description = "Detailed description of the vulnerability"
    val solution = "How to fix this vulnerability"
    val reference = "https://example.com/vuln-reference"
    val otherInfo = "Additional context: param=$param, value=$attackValue"
    val pluginId = 1000000 // Custom Plugin ID (use unique IDs for each script)

    activeScanner.newAlert()
        .setPluginId(pluginId)
        .setRisk(risk)
        .setConfidence(confidence)
        .setName(title)
        .setDescription(description)
        .setEvidence(evidence)
        .setOtherInfo(otherInfo)
        .setSolution(solution)
        .setReference(reference)
        .setMessage(msg)
        .raise()
}
```

### **Required Functions and Signatures**

Active scripts must implement two entry point functions. HawkScan calls these functions during the scanning process:

#### 1. `scanNode` - Endpoint-Level Testing

This function is called once per discovered endpoint/node. Use it to test the endpoint as a whole without modifying individual parameters.

```kotlin
fun scanNode(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage)
```

**Parameters:**

- `activeScanner` - Helper object providing methods to send requests and raise alerts
- `origMessage` - Original HTTP request to the endpoint (should not be modified directly)

**Common Use Cases:**

- Testing endpoints that don’t have parameters
- Checking for authentication/authorization issues at the endpoint level
- Testing HTTP methods (e.g., trying DELETE on a GET endpoint)
- Verifying endpoint-level security controls

**Important:** Always clone the original message before sending:

```kotlin
val msg = origMessage.cloneRequest()
```

#### 2. `scan` - Parameter-Level Testing

This function is called for each parameter in each request. Use it to test how the application handles modified parameter values.

```kotlin
fun scan(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage, param: String, value: String)
```

**Parameters:**

- `activeScanner` - Helper object providing methods to send requests and raise alerts
- `origMessage` - Original HTTP request (should not be modified directly)
- `param` - Name of the parameter being tested (e.g., “userId”, “search”)
- `value` - Original value of the parameter

**Common Use Cases:**

- Parameter fuzzing with custom payloads
- Testing injection vulnerabilities
- Boundary value testing
- Type confusion testing
- Business logic testing via parameter manipulation

### **The ScriptsActiveScanner Helper Object**

The `activeScanner` parameter provides essential methods for active scanning:

#### Key Methods:

**`setParam(HttpMessage msg, String param, String value)`**

- Modifies a parameter value in the request
- Handles all parameter types: URL query params, POST body params, JSON fields
- Example: `activeScanner.setParam(msg, "userId", "admin")`

**`sendAndReceive(HttpMessage msg, boolean followRedirects, boolean handleAntiCSRF)`**

- Sends the HTTP request and blocks waiting for the response
- `followRedirects` - Whether to automatically follow 3xx redirects (usually `false` for testing)
- `handleAntiCSRF` - Whether to automatically handle CSRF tokens (usually `false` for custom testing)
- Example: `activeScanner.sendAndReceive(msg, false, false)`

**`newAlert()`**

- Returns an alert builder for creating vulnerability findings
- Must set: pluginId, risk, confidence, name, message
- Should set: description, evidence, solution, reference
- Call `.raise()` to submit the alert

**`getBaseMsg()`**

- Gets the base message for the current scan
- Useful for accessing original request context

### **Advanced Example: Parameter Fuzzer with Error Detection**

This example demonstrates a complete fuzzing script that generates dynamic payloads and detects server errors:

```kotlin
import com.github.javafaker.Faker
import com.stackhawk.hste.extension.script.ScriptVars
import com.stackhawk.hste.extension.scripts.scanrules.ScriptsActiveScanner
import org.apache.log4j.LogManager
import org.parosproxy.paros.network.HttpMessage

val logger = LogManager.getLogger("fuzzer")
val faker = Faker()
val scriptVars = ScriptVars.getScriptVars("fuzzer.kts")

fun scanNode(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage) {
    // This fuzzer operates at the parameter level, so scanNode does nothing
    logger.debug("scanNode: ${origMessage.requestHeader.uri}")
    return
}

fun scan(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage, param: String, value: String) {
    logger.debug("Fuzzing parameter: $param (original value: $value)")

    // Get configuration from script variables
    val iterations = scriptVars["iterations"]?.toInt() ?: 10
    val stringStartLength = scriptVars["stringStartLength"]?.toInt() ?: 1
    val stringEndLength = scriptVars["stringEndLength"]?.toInt() ?: 100

    // Perform multiple fuzzing attempts
    (1..iterations).forEach { i ->
        // Clone the original request for each iteration
        val msg = origMessage.cloneRequest()

        // Generate dynamic fuzzing payload
        val fuzzedParamValue = if (i % 2 == 0) {
            // Even iterations: Random string of variable length
            faker.lorem().characters(stringStartLength, stringEndLength)
        } else {
            // Odd iterations: Interesting test data (e.g., Harry Potter spells)
            faker.harryPotter().spell()
        }

        // Set the fuzzing payload as the parameter value
        if (param.isNotBlank()) {
            activeScanner.setParam(msg, param, fuzzedParamValue)
        }

        try {
            // Send the fuzzed request
            activeScanner.sendAndReceive(msg, false, false)

            // Check for server errors (5xx status codes)
            if (msg.responseHeader.statusCode >= 500) {
                logger.info("Found 5xx error with payload: $fuzzedParamValue")
                logger.debug("Request: ${msg.requestHeader}${msg.requestBody}")

                // Raise an alert for the detected vulnerability
                raiseAlert(
                    activeScanner,
                    msg,
                    msg.responseHeader.primeHeader,
                    param,
                    fuzzedParamValue
                )

                logger.debug("Response: ${msg.responseHeader.statusCode} ${msg.responseBody}")
            }
        } catch (e: Exception) {
            logger.error("Error sending fuzzed request: ${e.message}")
        }
    }
}

fun raiseAlert(activeScanner: ScriptsActiveScanner, msg: HttpMessage, evidence: String, param: String, fuzzedParam: String) {
    val risk = 2 // Medium risk
    val confidence = 3 // High confidence
    val title = "Server Error Triggered by Fuzzing"
    val description = "The fuzzer detected a 5xx server error when testing parameter '$param' " +
                     "with value '$fuzzedParam'. This indicates the application does not properly " +
                     "handle unexpected input, which could lead to denial of service or information disclosure."
    val solution = "Implement proper input validation and error handling. Never expose stack traces " +
                  "or detailed error messages to users. Return generic error messages and log detailed " +
                  "errors server-side only."
    val reference = "https://owasp.org/www-project-top-ten/2017/A6_2017-Security_Misconfiguration"
    val otherInfo = "Fuzzed parameter: $param=$fuzzedParam"
    val pluginId = 1000000

    activeScanner.newAlert()
        .setPluginId(pluginId)
        .setRisk(risk)
        .setConfidence(confidence)
        .setName(title)
        .setDescription(description)
        .setEvidence(evidence)
        .setOtherInfo(otherInfo)
        .setSolution(solution)
        .setReference(reference)
        .setMessage(msg)
        .raise()
}
```

### **Script Logic Detailed Breakdown**

#### Configuration via Script Variables

```kotlin
val scriptVars = ScriptVars.getScriptVars("fuzzer.kts")
val iterations = scriptVars["iterations"]?.toInt() ?: 10
```

Script variables come from the `hawkAddOn.scripts.vars` section in `stackhawk.yml`. This allows configuration of script behavior without modifying code.

#### Request Cloning

```kotlin
val msg = origMessage.cloneRequest()
```

**Critical:** Never modify `origMessage` directly. Always clone it first. HawkScan reuses the original message for multiple tests.

#### Dynamic Payload Generation

```kotlin
val fuzzedParamValue = if (i % 2 == 0) {
    faker.lorem().characters(stringStartLength, stringEndLength)
} else {
    faker.harryPotter().spell()
}
```

Using JavaFaker library (included in SDK) to generate realistic but random test data. This is more effective than static payloads.

#### Parameter Modification

```kotlin
activeScanner.setParam(msg, param, fuzzedParamValue)
```

The `setParam` method intelligently handles different parameter types:

- URL query parameters: `?userId=123`
- POST form data: `userId=123`
- JSON body fields: `{"userId": "123"}`
- XML elements (with appropriate parsing)

#### Response Analysis

```kotlin
if (msg.responseHeader.statusCode >= 500) {
    raiseAlert(...)
}
```

Check for vulnerability indicators in responses:

- Status codes (500, 403, 401, etc.)
- Error messages or stack traces in response body
- Unexpected data disclosure
- Changed application behavior

#### Alert Builder Pattern

```kotlin
activeScanner.newAlert()
    .setPluginId(pluginId)
    .setRisk(risk)
    .setConfidence(confidence)
    // ... more fields
    .raise()
```

The builder pattern allows flexible alert construction. Required fields will cause exceptions if missing.

### **Advanced Example: Multi-Tenancy Violation Detection**

This example tests for authorization boundary violations:

```kotlin
import org.apache.log4j.Logger
import org.parosproxy.paros.network.HttpMessage
import com.stackhawk.hste.extension.scripts.scanrules.ScriptsActiveScanner

val logger = Logger.getLogger("tenancy-check")

fun scanNode(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage) {
    // Not used for this check
    return
}

fun scan(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage, param: String, value: String) {
    val uri = origMessage.requestHeader.uri
    logger.debug("Testing tenancy boundary for: $uri, param=$param")

    // Clone the request
    val msg = origMessage.cloneRequest()

    // Modify the parameter to reference a different user/tenant
    // In a real scenario, the script would have a list of test user IDs to try
    activeScanner.setParam(msg, param, "differentUserId")

    // Ensure JSON content type if needed
    msg.requestHeader.setHeader("Content-Type", "application/json")

    // Send the modified request
    activeScanner.sendAndReceive(msg, false, false)

    // Check if the response contains data that shouldn't be accessible
    val responseBody = msg.responseBody.toString()
    val forbiddenDataPattern = "12345678" // Example: looking for another user's ID

    if (msg.responseHeader.statusCode == 200 && responseBody.contains(forbiddenDataPattern)) {
        logger.warn("Tenancy violation detected!")
        val evidenceIdx = responseBody.indexOf(forbiddenDataPattern)
        val evidence = responseBody.substring(evidenceIdx, evidenceIdx + forbiddenDataPattern.length)

        raiseAlert(activeScanner, msg, evidence, param)
    }
}

fun raiseAlert(activeScanner: ScriptsActiveScanner, msg: HttpMessage, evidence: String, param: String) {
    val risk = 3 // High risk
    val confidence = 3 // High confidence
    val title = "Multi-Tenancy Boundary Violation"
    val description = "The authenticated user was able to access data belonging to another user " +
                     "by modifying the '$param' parameter. This indicates insufficient authorization " +
                     "checks and could allow lateral movement between user accounts."
    val solution = "Implement proper authorization checks on all data access operations. Verify that " +
                  "the authenticated user has permission to access the requested resource. Use " +
                  "centralized authorization logic and avoid client-side access control."
    val reference = "https://owasp.org/www-project-top-ten/2017/A5_2017-Broken_Access_Control"
    val otherInfo = "Evidence of unauthorized data access found in response"
    val pluginId = 1000001

    activeScanner.newAlert()
        .setPluginId(pluginId)
        .setRisk(risk)
        .setConfidence(confidence)
        .setName(title)
        .setDescription(description)
        .setEvidence(evidence)
        .setOtherInfo(otherInfo)
        .setSolution(solution)
        .setReference(reference)
        .setMessage(msg)
        .raise()
}
```

### **HawkScan Configuration**

Active scripts require **plugin registration** and configuration in the `hawkAddOn` section of `stackhawk.yml`:

#### **Step 1: Register the Plugin**

Before using an active script, registration is required to obtain a Plugin ID:

```bash
hawk register plugin fuzzer.kts
```

Response:

```
New Script Id: 1000014
- Add this ID to the StackHawk YAML under hawkAddOn: .scripts.-id
- Add this ID to the Plugin Script under pluginId
```

#### **Step 2: Add Plugin ID to Script Code**

In the script’s alert raising code, add the Plugin ID:

```kotlin
activeScanner.newAlert()
    .setPluginId(1000014)          // REQUIRED - ID from registration
    .setRisk(2)
    .setName("Custom Vulnerability")
    .setMessage(msg)
    .raise()
```

#### **Step 3: Configure in stackhawk.yml**

```yaml
hawkAddOn:
  scripts:
    - name: "fuzzer.kts"
      id: 1000014                  # REQUIRED - Plugin ID from registration
      type: active
      path: "hawkscripts"          # Parent directory only (will look in hawkscripts/active/)
      language: KOTLIN
      vars:                        # Accessed via ScriptVars.getScriptVars() - array structure
        - name: iterations
          val: "10"
        - name: stringStartLength
          val: "1"
        - name: stringEndLength
          val: "100"
```

**Configuration Parameters:**

- **`name`**: Script filename - must match registration name
- **`id`**: **REQUIRED** - Plugin ID obtained from `hawk register plugin`
- **`type`**: Must be `active`
- **`path`**: **Parent directory only** - type subdirectory added automatically (e.g., “hawkscripts” not “hawkscripts/active”)
- **`language`**: `KOTLIN`
- **`vars`**: Optional variables accessed via `ScriptVars.getScriptVars()`

**Important Notes:**

- Limit: 50 custom scripts per organization (shared with passive scripts)
- The Plugin ID must be added to BOTH the config file and script code
- Registration name should match the script filename

### **Alert Risk and Confidence Levels**

Understanding how to set appropriate risk and confidence levels:

#### Risk Levels (0-3):

- **0 (Informational)**: No direct security impact, but noteworthy
- **1 (Low)**: Minor security issue, difficult to exploit
- **2 (Medium)**: Moderate security issue, requires specific conditions
- **3 (High)**: Critical security vulnerability, easily exploitable

#### Confidence Levels (0-4):

- **0 (False Positive)**: Alert is likely incorrect
- **1 (Low)**: Suspicious behavior, needs verification
- **2 (Medium)**: Likely vulnerability, manual review recommended
- **3 (High)**: Strong evidence of vulnerability
- **4 (Confirmed)**: Definitively verified vulnerability

**Important:** Both risk and confidence must be > 0, or the alert will not be included in scan results.

### **Common Patterns and Best Practices**

#### Pattern 1: Iterative Testing with Variable Payloads

```kotlin
val payloads = listOf(
    "'OR'1'='1",
    "<script>alert(1)</script>",
    "../../etc/passwd",
    "\${7*7}",
    "A".repeat(10000)
)

payloads.forEach { payload ->
    val msg = origMessage.cloneRequest()
    activeScanner.setParam(msg, param, payload)
    activeScanner.sendAndReceive(msg, false, false)

    if (detectVulnerability(msg, payload)) {
        raiseAlert(activeScanner, msg, payload)
    }
}
```

#### Pattern 2: Response Time Analysis (Blind Vulnerabilities)

```kotlin
val startTime = System.currentTimeMillis()
activeScanner.sendAndReceive(msg, false, false)
val endTime = System.currentTimeMillis()
val responseTime = endTime - startTime

if (responseTime > 5000) {
    // Possible time-based blind SQL injection or DoS
    raiseAlert(activeScanner, msg, "Response time: ${responseTime}ms")
}
```

#### Pattern 3: Conditional Alert Raising

```kotlin
// Only raise alerts for specific conditions
if (msg.responseHeader.statusCode >= 500 &&
    msg.responseBody.toString().contains("SQLException")) {
    // Higher confidence - both status code and error message present
    raiseAlert(activeScanner, msg, evidence, risk = 3, confidence = 4)
} else if (msg.responseHeader.statusCode >= 500) {
    // Lower confidence - only status code
    raiseAlert(activeScanner, msg, evidence, risk = 2, confidence = 2)
}
```

#### Pattern 4: Using Helper Functions for Reusability

```kotlin
fun detectSQLInjection(msg: HttpMessage): Boolean {
    val body = msg.responseBody.toString()
    val indicators = listOf("SQL syntax", "ORA-", "SQLException", "mysql_fetch")
    return indicators.any { body.contains(it, ignoreCase = true) }
}

fun detectXSS(msg: HttpMessage, payload: String): Boolean {
    return msg.responseBody.toString().contains(payload)
}

// Use in scan function
if (detectSQLInjection(msg)) {
    raiseAlert(activeScanner, msg, "SQL error detected")
}
```

### **Troubleshooting**

#### Issue: “Script not being executed during scans”

**Symptoms:** The active script’s log messages don’t appear, no alerts are raised.

**Solutions:**

1. Verify `type: active` is set in the hawkAddOn configuration
2. Check that the script path is correct relative to project root
3. Look for compilation errors at HawkScan startup in `hawkscan.log`
4. Ensure both `scan` and `scanNode` functions are defined
5. Verify script filename matches the `name` in configuration

#### Issue: “Alerts not appearing in scan results”

**Symptoms:** Script executes, logs show alert was raised, but no findings in output.

**Solutions:**

1. Verify both `risk` and `confidence` are greater than 0
2. Ensure `.setMessage(msg)` is called on the alert builder
3. Check that `.raise()` is called at the end of the alert builder chain
4. Verify pluginId is unique and not conflicting with built-in rules
5. Check alert threshold settings in `stackhawk.yml`

#### Issue: “Too many false positives”

**Symptoms:** Script raises alerts for every request, many are incorrect.

**Solutions:**

1. Add more specific vulnerability detection logic
2. Increase the confidence level requirements
3. Filter out expected responses (e.g., legitimate 404s)
4. Use multiple indicators before raising an alert (status code + error message)
5. Test against known-good requests first to calibrate detection

#### Issue: “Script causes scanner to hang or timeout”

**Symptoms:** Scans take extremely long or time out when script is enabled.

**Solutions:**

1. Reduce the number of iterations per parameter
2. Add timeout handling for long-running operations
3. Use `try-catch` blocks around all network operations
4. Avoid infinite loops or recursive calls
5. Limit the scope with `scanNode` vs `scan` appropriately

#### Issue: “Parameters not being modified correctly”

**Symptoms:** `setParam` doesn’t seem to change the parameter value.

**Solutions:**

1. Verify that `setParam` gets called on a cloned message, not the original
2. Check that the parameter name matches exactly (case-sensitive)
3. For JSON bodies, ensure content-type is set correctly
4. Log the request body after `setParam` to verify changes
5. Some parameters may be in headers rather than body/query

### **Testing Active Scripts**

#### Development Workflow

1. **Start with a minimal script:**

   ```kotlin
   fun scan(activeScanner: ScriptsActiveScanner, origMessage: HttpMessage, param: String, value: String) {
       logger.info("Testing param: $param")
   }
   ```

2. **Test against a single endpoint:**

   ```yaml
   app:
     includePaths:
       - "/api/test"
   ```

3. **Add payload generation and logging:**

   ```kotlin
   val msg = origMessage.cloneRequest()
   activeScanner.setParam(msg, param, "test_value")
   logger.info("Modified request: ${msg.requestHeader}${msg.requestBody}")
   ```

4. **Send requests and analyze responses:**

   ```kotlin
   activeScanner.sendAndReceive(msg, false, false)
   logger.info("Response status: ${msg.responseHeader.statusCode}")
   logger.info("Response body: ${msg.responseBody}")
   ```

5. **Add vulnerability detection logic:**

   ```kotlin
   if (msg.responseHeader.statusCode >= 500) {
       logger.warn("Found vulnerability!")
       raiseAlert(activeScanner, msg, evidence)
   }
   ```

#### Testing Commands

```bash
# Run a limited scan for testing
hawk scan --config stackhawk.yml --spider-max 5

# Review logs for the script
tail -f hawkscan.log | grep "fuzzer"

# Test against a specific path
hawk scan --config stackhawk.yml --include-paths "/api/test"
```

### **Key Objects Reference**

For detailed information on classes and methods used in active scripts, see [Key Objects Documentation](https://docs.stackhawk.com/scripting/key-objects-scripting/):

- **ScriptsActiveScanner** - Helper object for active scanning operations
- **HttpMessage** - HTTP request/response encapsulation
- **HttpRequestHeader / HttpResponseHeader** - Header manipulation
- **ScriptVars** - Access to script configuration variables

### **Additional Resources**

- [GitHub Examples Repository](https://github.com/kaakaww/hawkscan-examples/tree/main/scripts/examples/active)
- [Working with the HawkScript SDK](https://docs.stackhawk.com/scripting/working-with-sdk/)
- [Passive Scanning Scripts](https://docs.stackhawk.com/scripting/script-types/passive-scripting/) - For non-invasive vulnerability detection
