Passive Scanning Scripts

Passive Script Type Details

Usage Overview

The passive script type allows the user to create new alert logic using non-destructive, monitor-only techniques. This script type is used to evaluate HTTP message structure or content from existing messages in the history database. Typically these messages are populated during the target discovery phase of the test cycle.

The required functions for this particular plugin are as follows:

  1. scan: this is the entry point for the script and defines the baseline logic for triggering an alert.
  2. appliesToHistoryType: defines logic to customize which message types from the history DB are inspected in the scan function. Using the provided template, this function defaults to including all message types.

Usage Scenarios

  1. Sensitive/Arbitrary Data detection: Although StackHawk includes limited sensitive data leakage detection out-of-the-box, customers may wish to enhance the detection of sensitive data or secrets that are of particular interest or concern for their own environments. These data “tokens” could include a wide range of PII data types, cloud secrets such as AWS or Google API keys, bank account information, or anything that the user can create a solid regex to express.

    Example: Detecting credit card numbers, social security numbers, API keys, or internal employee IDs in HTTP responses.

    Reference: sensitiveDataDisclosure.kts example: Coming Soon!

  2. Header Validation: Internal policies may dictate specific requirements for things such as JWT hash algorithms or inclusion of alternative security headers. These can be easily tested for using HawkScan’s passive scripts.

    Example: Enforcing Content Security Policy (CSP) headers, checking for secure JWT algorithms (HS256 vs RS256), validating HSTS configuration, or ensuring all API responses include proper CORS headers.

    Use Case: A company policy requires all API responses to include X-Content-Type-Options: nosniff and X-Frame-Options: DENY. A passive script checks every response and raises an alert if these headers are missing.

  3. Compliance and Business Logic Validation: Beyond standard security checks, passive scripts can validate application-specific compliance requirements or detect business logic issues that appear in responses.

    Example:

    • Detecting PCI DSS violations (e.g., full credit card numbers in logs or error messages)
    • Identifying HIPAA violations (e.g., unmasked patient identifiers in responses)
    • Finding debug information or stack traces in production responses
    • Detecting hardcoded secrets or internal URLs in JavaScript files
    • Validating API versioning headers are present
    • Checking for deprecated API warnings in responses

    Use Case: A healthcare application must never expose patient IDs in URLs or response bodies except when properly masked. A passive script scans all responses for unmasked patient ID patterns and raises HIPAA violation alerts.

See the GitHub hawkscan-examples repository for more detailed examples.

Simple / Template Script Example

Consider the following example/template script, passive_template.kts. It defines the required function interface and includes a useful optional helper function.

import net.htmlparser.jericho.Source //required
import org.parosproxy.paros.network.HttpMessage //required
import com.stackhawk.hste.extension.scripts.scanrules.ScriptsPassiveScanner //required
import org.parosproxy.paros.core.scanner.Plugin
import org.apache.log4j.LogManager

/*
 * Set up some default global values
 * Use the logger val to add messaging to the logs
 * with .info(), .debug(), etc.
 */

val logger = LogManager.getLogger("PassiveScanner Template")

var RISK = 2 // 0: info, 1: low, 2: medium, 3: high
var CONFIDENCE = 3 // 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed
val TITLE = "" //customize appropriately
val DESCRIPTION = "User was able to access users not in their company"
val SOLUTION = "Enforce tenancy"
val REFERENCE = "https://personal.rhul.ac.uk/vsai/149/Multi-tenancy%20doc%20300614.pdf"
val OTHER = "see: https://www.cloudreach.com/en/blog/multi-tenant-security-in-the-cloud-what-you-need-to-know/"
val PLUGIN_ID = 10000000 //Custom Plugin ID gets changed after plugin is registered using "hawk register plugin", and should be an Int

/**
 * Required function/required signature.  This is the entry point for the plugin.
 * Passively scans an HTTP message. The scan function will be called for
 * request/response made via HSTE, actual messages depend on the function
 * "appliesToHistoryType", defined below.
 *
 * @param ps - the PassiveScan parent object that will do all the core interface tasks
 *     (i.e.: providing access to Threshold settings, raising alerts, etc.).
 *     This is an ScriptsPassiveScanner object.
 * @param msg - the HTTP Message being scanned. This is an HttpMessage object.
 * @param src - the Jericho Source representation of the message being scanned.
 */
fun scan(
    ps: ScriptsPassiveScanner,
    msg: HttpMessage,
    src: Source
): Unit {
    // Test the request and/or response here
    ps.addHistoryTag("tag")
    logger.info("Attached tag to message")
    // Raise less reliable alert (that is, prone to false positives) when in LOW alert threshold
    // Expected values: "LOW", "MEDIUM", "HIGH"
    if (ps.getAlertThreshold() == Plugin.AlertThreshold.LOW) {
        // ...
    }
}

/**
 * Tells whether the scanner applies to the given history type.
 *
 * @param {Number} historyType - The ID of the history type of the message to be scanned.
 * @return {boolean} Whether the message with the given type should be scanned by this scanner.
 *
 * This function signature must be defined, but if left as-is, the plugin will default to testing
 * all history types
 */
fun appliesToHistoryType(
    historyType: Int
): Boolean {
    // For example, to just scan spider messages:
    // return historyType == org.parosproxy.paros.model.HistoryReference.TYPE_SPIDER;

    // Default behaviour scans all default types.
    return ScriptsPassiveScanner.getDefaultHistoryTypes().contains(historyType)
}

/**
 * alert is an optional helper function that can be customized or removed.
 * It does not require a specific signature
 * */
fun alert(
    ps: ScriptsPassiveScanner,
    msg: HttpMessage,
    src: Source
): Unit {

    /**
     * newAlert() function call must set the registered plugin ID,
     * must include risk and confidence which are both > 0,
     * and must set the http message
     */
    ps.newAlert()
        .setPluginId(PLUGIN_ID)
        .setRisk(RISK)
        .setConfidence(CONFIDENCE)
        .setName(TITLE)
        .setDescription(DESCRIPTION)
        .setParam("The param")
        .setEvidence("Evidence")
        .setOtherInfo(OTHER)
        .setSolution(SOLUTION)
        .setReference(REFERENCE)
        .setMessage(msg)
        .setCweId(0)
        .setWascId(0)
        .raise();
}

Function signature breakdown:

fun scan(
    ps: ScriptsPassiveScanner,
    msg: HttpMessage,
    src: Source
): Unit {

The scan() function is the required entry point for the passive scan type. The function inherits three objects when the script is invoked by HawkScan.

  1. The ScriptsPassiveScanner object comes from the com.stackhawk.hste.extension.scripts.scanrules.ScriptsPassiveScanner class. It is a helper object that provides access to the History database and related message types, as well as defining the default newAlert() method, which allows the user to set all of the required data parameters for an alert and to raise the alert. The invocation of newAlert() is often delegated to an optional helper function, but this is not required.
  2. The msg is an HttpMessage object referencing the current message under investigation. The msg can be unpacked to any arbitrary level to inspect the header or body response content and create appropriate alerts. More details on HttpMessages can be found in the Key Objects documentation.
  3. The Source object comes from net.htmlparser.jericho.Source class. If the body of the response contains an HTML document, src will be a Jericho-standard structured representation of the HTML, allowing the user to interact with the contents in an object-oriented manner, including iterating or searching through tags and elements without having to conduct pre-processing.

The appliesToHistoryType() function is required, but frequently left in the default form. By default, the function applies passive testing to all default history types.

fun appliesToHistoryType(
    historyType: Int
): Boolean {
    // For example, to just scan spider messages:
    // return historyType == org.parosproxy.paros.model.HistoryReference.TYPE_SPIDER;

    // Default behaviour scans default types.
    return ScriptsPassiveScanner.getDefaultHistoryTypes().contains(historyType)
}

This function takes an integer, representing the historyType and returns a Boolean which can be matched to proceed with or cancel the test cycle. The default History types include:

TYPE_PROXIED //a message proxied through HawkScan
TYPE_SPIDER //a message discovered via Base Spider
TYPE_ZAP_USER //messages generated from within the test engine
TYPE_AJAX_SPIDER //message discovered or sent via the AJAX spider

There are other history types not included in the default set which can be referenced using org.parosproxy.paros.model.HistoryReference.<type>. Types can be found in the HistoryReference class.

HawkScan Configuration

Passive scripts require plugin registration and configuration in the hawkAddOn section of stackhawk.yml:

Step 1: Register the Plugin

Before using a passive script, you must register it to obtain a Plugin ID:

hawk register plugin sensitiveDataDisclosure.kts

Response:

New Script Id: 1000015
- Add this ID to your StackHawk YAML under hawkAddOn: .scripts.-id
- Add this ID to your Plugin Script under pluginId

Step 2: Add Plugin ID to Script Code

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

ps.newAlert()
    .setPluginId(1000015)          // REQUIRED - ID from registration
    .setRisk(2)
    .setName("Sensitive Data Disclosure")
    .setMessage(msg)
    .raise()

Step 3: Configure in stackhawk.yml

hawkAddOn:
  scripts:
    - name: "sensitiveDataDisclosure.kts"
      id: 1000015                  # REQUIRED - Plugin ID from registration
      type: passive
      path: "hawkscripts"          # Parent directory only (will look in hawkscripts/passive/)
      language: KOTLIN
      vars:                        # Accessed via ScriptVars.getScriptVars() - array structure
        - name: alert_threshold
          val: "medium"
        - name: scan_responses
          val: "true"
        - name: scan_requests
          val: "false"

Configuration Parameters:

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

Important Notes:

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

Complete Configuration Example

app:
  host: https://api.example.com
  includePaths:
    - "/api/.*"

hawkAddOn:
  scripts:
    - name: "sensitiveDataDisclosure.kts"
      id: 1000015                  # REQUIRED - from hawk register plugin
      type: passive
      path: "hawkscripts"          # Parent directory only
      language: KOTLIN
      vars:
        - name: check_emails
          val: "true"
        - name: check_phone_numbers
          val: "true"
        - name: check_ssn
          val: "false"

    - name: "securityHeadersCheck.kts"
      id: 1000016                  # REQUIRED - from hawk register plugin
      type: passive
      path: "hawkscripts"          # Parent directory only
      language: KOTLIN
      vars:
        - name: require_csp
          val: "true"
        - name: require_hsts
          val: "true"
        - name: require_nosniff
          val: "true"

Advanced Example: Sensitive Data Detection

This comprehensive example demonstrates detecting multiple types of sensitive data with configurable patterns:

import net.htmlparser.jericho.Source
import org.parosproxy.paros.network.HttpMessage
import com.stackhawk.hste.extension.scripts.scanrules.ScriptsPassiveScanner
import org.apache.log4j.LogManager
import kotlin.text.Regex

val logger = LogManager.getLogger("sensitiveDataDisclosure")

// Static values for all findings
val cweId = 200 // CWE-200 Exposure of Sensitive Information
val wascId = 13 // WASC-13 Information Leakage
val pluginId = 1000058

// Data class for sensitive data patterns
class SensitiveDataBlob(
    val slug: String,
    val title: String,
    val description: String,
    val pattern: Regex,
    val solution: String = "Don't disclose sensitive information of type $slug",
    val reference: String = "",
    val other: String = "",
    val risk: Int = 2, // 0: info, 1: low, 2: medium, 3: high
    val confidence: Int = 3 // 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed
)

// Unwanted file types to skip for performance
val unwantedFiletypes = arrayOf(
    "image/png",
    "image/jpeg",
    "image/gif",
    "application/x-shockwave-flash",
    "application/pdf"
)

// Define patterns to search for
fun defineSearchParams(): List<SensitiveDataBlob> {
    val blobs = mutableListOf<SensitiveDataBlob>()

    blobs.add(
        SensitiveDataBlob(
            slug = "email",
            title = "Information Disclosure - Email Addresses",
            description = "User email addresses discovered in HTTP message body. " +
                         "Public disclosure of user emails can be a minor violation of PII, " +
                         "but can also disclose potential usernames or other internal state.",
            pattern = Regex("([a-zA-Z0-9_.+-]+@[a-zA-Z0-9]+[a-zA-Z0-9-]*[.][a-zA-Z0-9-.]*[a-zA-Z0-9]{2,})"),
            risk = 1
        )
    )

    blobs.add(
        SensitiveDataBlob(
            slug = "internalIPs",
            title = "Information Disclosure - Internal IP Addresses",
            description = "Internal IP addresses discovered in HTTP message body. " +
                         "This could reveal internal network topology and aid reconnaissance.",
            pattern = Regex("((172[.]\\d{1,3}[.]\\d{1,3}[.]\\d{1,3})|(192[.]168[.]\\d{1,3}[.]\\d{1,3})|(10[.]\\d{1,3}[.]\\d{1,3}[.]\\d{1,3})|([fF][eE][89aAbBcCdDeEfF]0?::))"),
            risk = 1
        )
    )

    blobs.add(
        SensitiveDataBlob(
            slug = "phoneNumbers",
            title = "Information Disclosure - Phone Numbers",
            description = "Potential PII in the form of personal phone numbers disclosed.",
            pattern = Regex("([+]\\d{1,2}\\s)?[(]?\\d{3}[)]?[\\s.-]\\d{3}[\\s.-]\\d{4}"),
            risk = 1
        )
    )

    blobs.add(
        SensitiveDataBlob(
            slug = "creditCard",
            title = "Information Disclosure - Credit Card Numbers",
            description = "Potential credit card numbers found in response. This is a serious " +
                         "PCI DSS violation and could lead to compliance penalties.",
            pattern = Regex("\\b(?:\\d{4}[\\s-]?){3}\\d{4}\\b"),
            solution = "Never transmit or log full credit card numbers. Use tokenization and " +
                      "only display last 4 digits for verification.",
            risk = 3,
            confidence = 2 // Lower confidence as pattern could match other number sequences
        )
    )

    return blobs
}

fun scan(ps: ScriptsPassiveScanner, msg: HttpMessage, src: Source) {
    val dataTypes: List<SensitiveDataBlob> = defineSearchParams()

    // Check content type to avoid scanning unwanted file types
    val contentType = msg.responseHeader.getHeader("Content-Type") ?: ""
    if (unwantedFiletypes.any { contentType.contains(it) }) {
        logger.debug("Skipping scan - unwanted content type: $contentType")
        return
    }

    // Scan response body for sensitive data patterns
    val body = msg.responseBody.toString()

    dataTypes.forEach { entry ->
        if (entry.pattern.containsMatchIn(body)) {
            logger.debug("Found match for ${entry.slug} using ${entry.pattern}")

            // Find all instances of the pattern
            val foundDisclosures = entry.pattern.findAll(body, 0)
            raiseAlert(ps, msg, src, entry, foundDisclosures)
        }
    }
}

fun raiseAlert(
    ps: ScriptsPassiveScanner,
    msg: HttpMessage,
    src: Source,
    blob: SensitiveDataBlob,
    foundDisclosures: Sequence<MatchResult>
) {
    logger.debug("Raising alert for ${blob.slug}")

    // Collect all found instances
    val evidence = mutableListOf<String>()
    foundDisclosures.forEach { entry -> evidence.add(entry.value) }

    // Build and raise the alert
    ps.newAlert()
        .setPluginId(pluginId)
        .setRisk(blob.risk)
        .setConfidence(blob.confidence)
        .setName(blob.title)
        .setDescription(blob.description)
        .setEvidence(evidence[0]) // First match as evidence
        .setOtherInfo("${blob.other}\n\nFound ${evidence.size} instance(s):\n${evidence.take(5).joinToString("\n")}")
        .setSolution(blob.solution)
        .setReference(blob.reference)
        .setCweId(cweId)
        .setWascId(wascId)
        .setMessage(msg)
        .raise()

    logger.info("Raised alert for ${blob.slug}: found ${evidence.size} instances in ${msg.requestHeader.uri}")
}

fun appliesToHistoryType(historyType: Int): Boolean {
    // Use default history types (spider, proxy, ajax spider, etc.)
    return ScriptsPassiveScanner.getDefaultHistoryTypes().contains(historyType)
}

Advanced Example: Security Headers Validation

This example checks for required security headers on API responses:

import net.htmlparser.jericho.Source
import org.parosproxy.paros.network.HttpMessage
import com.stackhawk.hste.extension.scripts.scanrules.ScriptsPassiveScanner
import org.apache.log4j.LogManager

val logger = LogManager.getLogger("securityHeadersCheck")
val pluginId = 1000060

data class HeaderCheck(
    val headerName: String,
    val required: Boolean,
    val expectedValues: List<String>? = null,
    val description: String,
    val risk: Int = 1
)

fun defineHeaderChecks(): List<HeaderCheck> {
    return listOf(
        HeaderCheck(
            headerName = "X-Content-Type-Options",
            required = true,
            expectedValues = listOf("nosniff"),
            description = "X-Content-Type-Options header missing or incorrect. " +
                         "This header prevents MIME-sniffing attacks.",
            risk = 1
        ),
        HeaderCheck(
            headerName = "X-Frame-Options",
            required = true,
            expectedValues = listOf("DENY", "SAMEORIGIN"),
            description = "X-Frame-Options header missing or incorrect. " +
                         "This header prevents clickjacking attacks.",
            risk = 2
        ),
        HeaderCheck(
            headerName = "Strict-Transport-Security",
            required = true,
            expectedValues = null, // Any value acceptable
            description = "Strict-Transport-Security (HSTS) header missing. " +
                         "This header enforces HTTPS connections.",
            risk = 2
        ),
        HeaderCheck(
            headerName = "Content-Security-Policy",
            required = true,
            expectedValues = null,
            description = "Content-Security-Policy header missing. " +
                         "CSP helps prevent XSS and data injection attacks.",
            risk = 2
        )
    )
}

fun scan(ps: ScriptsPassiveScanner, msg: HttpMessage, src: Source) {
    // Only check responses (not requests)
    if (!msg.responseHeader.isHeader) {
        return
    }

    // Only check HTML/API responses
    val contentType = msg.responseHeader.getHeader("Content-Type") ?: ""
    if (!contentType.contains("html") && !contentType.contains("json") && !contentType.contains("xml")) {
        logger.debug("Skipping non-HTML/API response")
        return
    }

    val headerChecks = defineHeaderChecks()

    headerChecks.forEach { check ->
        val headerValue = msg.responseHeader.getHeader(check.headerName)

        if (headerValue == null && check.required) {
            // Required header is missing
            raiseHeaderAlert(ps, msg, check, "Header not present")
        } else if (headerValue != null && check.expectedValues != null) {
            // Check if value matches expected values
            val valueMatches = check.expectedValues.any {
                headerValue.contains(it, ignoreCase = true)
            }
            if (!valueMatches) {
                raiseHeaderAlert(
                    ps,
                    msg,
                    check,
                    "Header present but value '$headerValue' does not match expected values: ${check.expectedValues.joinToString(", ")}"
                )
            }
        }
    }
}

fun raiseHeaderAlert(
    ps: ScriptsPassiveScanner,
    msg: HttpMessage,
    check: HeaderCheck,
    evidence: String
) {
    logger.info("Security header issue: ${check.headerName} - $evidence")

    ps.newAlert()
        .setPluginId(pluginId)
        .setRisk(check.risk)
        .setConfidence(3) // High confidence
        .setName("Missing or Incorrect Security Header: ${check.headerName}")
        .setDescription(check.description)
        .setEvidence(evidence)
        .setSolution("Add the ${check.headerName} header to all responses with appropriate values. " +
                    "Configure this at the web server or application framework level.")
        .setReference("https://owasp.org/www-project-secure-headers/")
        .setCweId(693) // CWE-693: Protection Mechanism Failure
        .setMessage(msg)
        .raise()
}

fun appliesToHistoryType(historyType: Int): Boolean {
    return ScriptsPassiveScanner.getDefaultHistoryTypes().contains(historyType)
}

Common Patterns and Best Practices

Pattern 1: Efficient Content-Type Filtering

// Always filter unwanted content types early for performance
val contentType = msg.responseHeader.getHeader("Content-Type") ?: ""
if (contentType.contains("image/") || contentType.contains("application/pdf")) {
    return // Skip binary files
}

Pattern 2: Using Jericho Source for HTML Parsing

import net.htmlparser.jericho.*

fun scan(ps: ScriptsPassiveScanner, msg: HttpMessage, src: Source) {
    // Use Jericho Source to parse HTML elements efficiently
    val scriptTags = src.getAllElements(HTMLElementName.SCRIPT)

    scriptTags.forEach { scriptTag ->
        val scriptContent = scriptTag.content?.toString() ?: ""

        // Check for hardcoded API keys in JavaScript
        if (scriptContent.contains("api_key", ignoreCase = true)) {
            ps.addHistoryTag("potential-hardcoded-secret")
            // Raise alert...
        }
    }
}

Pattern 3: Limiting Alert Volume

// Track alerts raised to avoid flooding
val alertsRaisedPerType = mutableMapOf<String, Int>()
val maxAlertsPerType = 10

fun raiseAlert(ps: ScriptsPassiveScanner, msg: HttpMessage, alertType: String) {
    val count = alertsRaisedPerType.getOrDefault(alertType, 0)

    if (count < maxAlertsPerType) {
        ps.newAlert()
            // ... build alert
            .raise()

        alertsRaisedPerType[alertType] = count + 1
    } else {
        logger.debug("Alert limit reached for $alertType, skipping")
    }
}

Pattern 4: Respecting Alert Threshold

import org.parosproxy.paros.core.scanner.Plugin

fun scan(ps: ScriptsPassiveScanner, msg: HttpMessage, src: Source) {
    val threshold = ps.getAlertThreshold()

    // Raise lower-confidence alerts only in LOW threshold mode
    if (threshold == Plugin.AlertThreshold.LOW) {
        // Check for potential issues with lower confidence
        checkForPotentialIssues(ps, msg)
    }

    // Always check for high-confidence issues
    checkForDefiniteIssues(ps, msg)
}

Troubleshooting

Issue: “Passive script not being called”

Symptoms: Log messages don’t appear, no alerts are raised.

Solutions:

  1. Verify type: passive in hawkAddOn configuration
  2. Check script path is correct relative to project root
  3. Look for compilation errors in hawkscan.log
  4. Ensure both scan and appliesToHistoryType functions are defined

Issue: “Too many alerts raised”

Symptoms: Scan results contain hundreds of duplicate alerts.

Solutions:

  1. Implement alert counting/limiting per pattern type
  2. Use .addHistoryTag() instead of raising alerts for every match
  3. Filter responses more aggressively (content-type, status code)
  4. Deduplicate alerts based on evidence or URL patterns

Issue: “Alerts not appearing in results”

Symptoms: Script runs, logs show alerts raised, but not in output.

Solutions:

  1. Verify both risk and confidence are > 0
  2. Ensure .setMessage(msg) is called on alert builder
  3. Check .raise() is called at the end
  4. Verify pluginId is unique and not conflicting

Issue: “Passive script causing performance issues”

Symptoms: Scans take significantly longer with script enabled.

Solutions:

  1. Filter unwanted content types at the start of scan()
  2. Use efficient regex patterns (avoid catastrophic backtracking)
  3. Limit the number of regex searches performed
  4. Use appliesToHistoryType() to reduce scope
  5. Cache compiled regex patterns outside the scan function

Testing Passive Scripts

Development Workflow

  1. Start with a minimal scanner:
    fun scan(ps: ScriptsPassiveScanner, msg: HttpMessage, src: Source) {
        logger.info("Scanning: ${msg.requestHeader.uri}")
        logger.info("Response status: ${msg.responseHeader.statusCode}")
        logger.info("Content-Type: ${msg.responseHeader.getHeader("Content-Type")}")
    }
    
  2. Test pattern matching:
    val testPattern = Regex("[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}")
    val body = msg.responseBody.toString()
    
    if (testPattern.containsMatchIn(body)) {
        val matches = testPattern.findAll(body)
        logger.info("Found ${matches.count()} matches")
        matches.forEach { logger.debug("Match: ${it.value}") }
    }
    
  3. Add alert raising:
    ps.newAlert()
        .setPluginId(1000050)
        .setRisk(1)
        .setConfidence(3)
        .setName("Test Alert")
        .setDescription("Testing alert mechanism")
        .setEvidence(matches.first().value)
        .setMessage(msg)
        .raise()
    logger.info("Alert raised successfully")
    

Key Objects Reference

For detailed information on classes and methods used in passive scripts, see Key Objects Documentation:

  • ScriptsPassiveScanner - Helper object for passive scanning operations
  • HttpMessage - HTTP request/response encapsulation
  • Source (Jericho) - HTML parsing and element access
  • Plugin.AlertThreshold - Alert threshold enumeration

Additional Resources