定制脚本

定制脚本可以使您的 DAST 扫描更加灵活。您可以添加 JavaScript 以在扫描期间操控 HTTP 请求或响应。此操作可在请求发送到服务器之前或收到响应之后完成。

如何为 AppScan 启用定制脚本

缺省情况下启用定制脚本(CustomScripts.Enable 设置设定为 True)。

要禁用脚本,请执行以下操作:

  1. 转至工具 > 选项> 高级选项卡。
  2. CustomScripts.Enable 设置设定为“False”。

如何为 DAST 扫描添加和启用定制脚本

您可以通过以下方式将定制 JavaScript 添加到 DAST 扫描:
  1. 导航到配置设置中的高级部分下的定制脚本
  2. 单击 + 添加按钮。
  3. 名称字段预先填充了脚本创建的日期和时间。您可以根据需要更改名称。
  4. 执行上下文下拉列表中,选择请求之前响应之后,具体取决于您希望脚本运行的时间。
  5. JavaScript 字段中,输入您的 JavaScript 代码。要接收 AppScan 环境中 JavaScript 代码的建议,请按 Ctrl+Space

    确保您的代码没有语法错误,并且与 AppScan 支持的 JavaScript 版本兼容。有关规则以及如何编写代码的更多信息,请参阅 JavaScript 代码

    示例 1:自动将登录详细信息添加到您的请求中(无需使用登录管理配置)。

    // Before each request
    if(request.method == "POST") {
        request.body = JSON.stringify({
            "username": "jsmith",
            "password": "demo1234"
        })
    }

    示例 2:检查响应是否包含 API 密钥,并将其存储在全局变量中。

    // After each response
    try {
            var result = JSON.parse(response.body)
            log(`parsing: ${JSON.parse(response.body).Authorization}`)
            if(result.Authorization) {
                environment.globalVariables.addOrUpdate("Authorization", result.Authorization)
                log(`key = ${result.Authorization}`)
            }
    }
    catch (error) {
            log(error)
    }

    示例 3:将授权 API 密钥(如果存在)设置到标头。

    // Before each request
    var authorization = environment.globalVariables.get("Authorization")
        if(authorization) {
            request.headers.set("Authorization", authorization)
            log(`authorization is ${authorization}`)
        }
  6. 单击添加以保存您的脚本。该脚本将添加到列表中并缺省启用。
    Note: 在 DAST 扫描期间,您添加的脚本将针对每个 HTTP 请求或响应执行。
    合理使用此功能可以提高扫描的效率和效果。在实时扫描中部署脚本之前,请务必在受控环境中测试脚本,以避免出现任何意外行为或结果。
  7. 您可以使用启用复选框,从定制脚本列表中选择启用或禁用脚本。
  8. 您可以修改、删除或更改脚本的执行顺序。在脚本的右上角,单击 垂直三点菜单 并选择如下选项:
    • 编辑
    • 删除
    • 上移
    • 下移
  9. 单击开始全面扫描运行扫描。
  10. 结果:将针对每个请求或响应执行启用的脚本。您可以从数据 > 请求选项卡 > 请求/响应查看脚本的详细信息。
    Note: 当您导出模板中的定制脚本时,缺省情况下会对脚本进行加密。要手动禁用此加密,请从扫描配置设置中转到高级配置并设置常规:将加密敏感数据设为 False

JavaScript 代码

上下文

请求:您可以在请求数据发送到服务器之前读取或修改相应数据。每个请求都由一个请求对象表示,该对象具有以下属性:
  1. 标头:包含所有请求标头。除缺省标头外,您可以添加、删除或修改任何标头。
    // Add header
    request.headers.add(key, value)
    // Override header if exist
    request.headers.set(key, value)
    Note: 使用“添加”而不是“设置”会导致请求中出现标头的多个实例。因此,“设置”比“添加”更可取。
    // Remove header
    request.headers.remove(key)
    // Modify header
    request.headers[key]
    // Examples
    request.headers.add("Authorization", "MyCoolApiKey")
    request.headers.set("Authorization", "SecondApiKey")
    request.headers.remove("Authorization")
    request.headers['Authorization'] = "myapikey3"
    缺省标头(不区分大小写):这些是只读的,无法修改。
    • 主机:识别请求将发送到的主机。
    • 内容长度:计算请求的大小,并将该信息发送到服务器。
  2. 正文:包含有关请求正文的信息。正文为“字符串”类型。
    // Access body
    request.body
    // Example
    request.body = "{ "username":"user" }"
  3. 路径:保存有关请求所针对的端点的信息。端点格式不包括基本 URL(例如 /api/v1/test)。该属性是只读的;即使您在运行脚本之前尝试进行更改,它仍然会与首次设置时相同。
    // Access url path
    request.path
    // Example
    if (request.path.startsWith("api/v1/test"))
  4. 查询:保存当前请求的查询字符串和参数(如果有)。您可以在此对象中添加或删除查询参数。每个值都被视为单个对象,如“key=value”。当您使用“getQueryString”时,它会为您提供一个字符串,其中包含用“&”符号连接的所有项目。该字符串是项目的副本,因此如果更改该字符串,也只是更改查询的副本。
    Note: 如果查询参数为空,AppScan 将不会发送查询定界符(“?”)
    // Retrieve the current query string
    request.query.getQueryString()
    
    // Example
    // Add new value (or update existing one)
    request.query.addOrUpdate("appscan","appscanvalue")
    // Check if value exist
    request.query.contains("appscan") // for key only check
    request.query.contains("appscan=appscanvalue") // for key and value check
    // Remove an item
    request.query.remove("appscan") // for key only check
    request.query.remove("appscan=appscanvalue") // for key and value check
    request.query.clear() // remove all values
  5. 方法:请求的方法(例如 GET/POST/等)。该属性是只读的;即使您在运行脚本之前尝试进行更改,它仍然会与首次设置时相同。
    // Access to the method
    request.method
    // Example
    if (request.method == "GET")
响应:包含响应数据和相关父项请求的对象。您可以检索信息、修改或保存响应数据。每个响应都由一个响应对象表示,该对象具有以下属性:
  1. 正文:包含请求的响应正文。响应为字符串格式,如有必要,您需要手动进行转换(例如,转换为 JSON)。
    // Access body
    response.body
    // Example
    let json = JSON.parse(config.response)
  2. 路径:包含有关响应端点的信息。
    // Access to the relative url
    response.path
    // Example
    if (response.path.startsWith("api/v1/test"))
  3. 状态:存储有关响应状态的详细信息。状态是一个具有以下属性的对象:
    // Access status
    response.status.statusCode // A numeric representation of the status
    response.status.statusDescription // The description of the status code
    response.status.statusLine //the whole status line including the http version
    // Example
    if (response.status.statusCode == 200) // check only the number
    if (response.status.statusDescription == "OK") // Check only the description
    if (response.status.statusLine == "HTTP1.1 200 OK") // Full line
  4. 标头:包含所有响应标头。除缺省标头外,您可以添加、删除或修改任何标头。
    // Add header
    response.headers.add(key, value)
    // Override header if exist
    response.headers.set(key, value)
    // Remove header
    response.headers.remove(key)
    // Modify header
    response.headers[key]
    // Examples
    response.headers.add("Authorization", "MyCoolApiKey")
    response.headers.set("Authorization", "SecondApiKey")
    response.headers.remove("Authorization")
    response.headers['Authorization'] = "myapikey3"
  5. 父项请求:包含在响应之前发送的请求的对象。请求数据是只读的。该请求具有先前定义的请求对象的所有属性。
    // Example
    response.request.path //- access to the path from the request

环境数据

短期数据对象会存储重要信息,直到任务结束,例如当您暂停、停止或完成扫描、登录或运行手动测试时。

  1. 全局变量: 全局变量数据是一种键-值数据结构,旨在存储用户特定的数据对。键是唯一的(意味着没有两个元素可以具有相同的键,尽管多个元素可以共享相同的值),这对于在运行中的脚本之间共享数据非常有用。

    每次完成新扫描或重新扫描后,系统都会擦除这些数据。

    // Add data to the globals or update existing one
    environment.globalVariables.addOrUpdate(key, value)
    // Remove key data from the globals
    environment.globalVariables.remove(key)
    // Read data - retrieves the value for the current key (without removing it)
    environment.globalVariables.get(key)
  2. 扫描状态:包含当前扫描的相关信息。
    // determine if the current phase is explore phase
    environment.status.isInExplore()
    // determine if the current phase is test phase
    environment.status.isInTest()

日志记录

在脚本中启用日志记录功能以帮助进行调试和识别问题。“onsole.log”命令在这里不起作用。相反,请使用“log”一词,后跟带引号的消息。此外,如果引擎运行期间出现与 JavaScript 语法或使用无关的错误,该错误也会记录在引擎跟踪日志中。

// Log message to file
log("my cool logging")

// Log message with custom source
log("message", "my-cool-source")

约束

不要在脚本上下文中使用以下方法:
  • eval
  • 函数(构造函数)
  • setTimeout
  • setInterval
使用任何这些方法都会导致脚本出现故障并产生错误

错误处理

如果脚本使用任何受限制的函数并遇到错误或发生异常,将停止运行。此故障会暂停当前扫描,并将错误详细信息记录在“UserScript.log”文件中以供进一步检查。

加载配置了脚本的模板

您可以通过加载已启用脚本配置的模板来开始扫描。Program Files (x86)\HCL\AppScan Standard\Templates 中提供了一个示例模板,其中预先配置了 demo.testfire 描述文件的脚本。

AppScan 支持的 JavaScript 版本

目前支持 ES6 (2015) 至 ES15 (2024) 的 ECMAScript 版本,但以下功能除外:
  • ES6 (2015)
    • 生成器
    • 尾调用
  • ES8 (2017)
    • 共享内存和原子操作
  • ES15 (2024)
    • Atomics.waitAsync
    • 正则表达式标志 \v