定制脚本

定制脚本可以使您的 DAST 扫描更加灵活。您可以添加 JavaScript,以便在扫描过程中操纵 HTTP 请求和响应,无论是在向服务器发送请求之前还是在收到响应之后。

概述

定制脚本在 AppScan 中运行,并对内存中的请求和响应模型进行操作,使您可以通过编程方式调整请求和检查响应,而无需构建扩展或使用代理服务器。

概览

请求前:在将 HTTP 请求发送到应用程序之前执行脚本以修改该请求(添加/移除标头、加密数据、处理令牌等)。

响应后:从应用程序收到 HTTP 响应后处理该响应(解密内容、提取值等)。

参数:仅使用 JavaScript 生成多步骤操作表单填充器的值。

合理使用此功能可以提高扫描的效率和效果。在实时部署之前,在受控环境中测试脚本,以确保它们按预期运行。

模板路径示例: C:\Program Files (x86)\HCL\AppScan Standard\Templates.

定制脚本示例:GitHub 存储库

添加定制脚本

将定制 JavaScript 添加到您的 DAST 扫描中,如下所示:
  1. 在配置设置中的高级部分下,选择定制脚本
  2. 单击 + 添加按钮。
  3. 如有需要,可修改预填的名称字段。
  4. 为脚本选择适当的执行上下文::
    1. 请求前脚本:在发送请求前运行脚本。
    2. 响应后:在收到响应后运行脚本。
    3. 参数:用于包含多个步骤或填写表单的任务。您可以将此上下文与 setResultValue 方法结合使用,以便脚本将结果返回给 AppScan
  5. JavaScript 字段(代码编辑器)中输入JavaScript,并使用 Ctrl+空格键在 AppScan 环境中自动完成建议。
  6. 单击添加以保存您的脚本。该脚本将添加到列表中并缺省启用。
  7. 您可以使用启用复选框,从定制脚本列表中选择启用或禁用脚本。
  8. 使用启用复选框,或通过在脚本列表中编辑、删除脚本或重新排序脚本来管理脚本。 选项菜单
  9. 单击开始全面扫描运行扫描。
  10. 结果:将针对每个请求或响应执行启用的脚本。您可以从数据 > 请求选项卡 > 请求/响应查看脚本的详细信息。

    请验证脚本的语法是否正确,并确认其与 AppScan 支持的 JavaScript 版本兼容。如需详细指导,请参阅 JavaScript 代码。

    注:
    • 定制脚本保存在扫描配置文件中,可使用模板导出。它们不包括在扫描结果中。
    • 缺省情况下,在模板中导出定制脚本时,将对其进行加密。要禁用加密,请在扫描配置中将“常规:加密敏感数据”设置设为 False

激活定制脚本

定制脚本缺省情况下处于启用状态,并在录制和扫描过程中对每个 HTTP 请求和响应执行。

要禁用脚本执行,请转到工具 > 选项 > 高级选项卡,然后将 CustomScripts.Enable 设置为“False”。

执行上下文

请求前

用途:修改传出的 HTTP 请求。

数据:只有请求和环境对象可用,其他对象将为 null。
// Attach Authorization from stored global variable
var token = environment.globalVariables.get("Authorization");
if (token) request.headers.set("Authorization", token);

响应后

用途:检查响应和持续值。

数据:只有响应和环境数据对象可用。其他对象将为 null。
// Capture token from JSON response and store it for reuse
try {
		var result = JSON.parse(response.body);
		if (result && result.Authorization) {
			environment.globalVariables.addOrUpdate("Authorization", result.Authorization);
		}
} catch(e) { log(e); }

参数

用途:多步骤操作表单填充器生成值。
  • 此上下文中没有可用的数据对象(请求或响应);使用它们将返回 null。
  • 通过 setResultValue(value) 返回值。

在表单填充器中,当您选择值源 > 定制脚本时,列表仅显示其执行上下文为参数的脚本。

在多步骤操作中,您还可以使用参数脚本提供动态值。
// Example: Return a unique email for registration flows
function random(n) { return Math.random().toString(36).slice(2, 2+n); }
var email = "test_" + Date.now() + "_" + random(6) + "@example.test";
setResultValue(email);

API 参考文档

请求对象(请求上下文之前)- 完整 API
// Examples:
// Handle headers
request.headers.set("Authorization", "Bearer");
request.headers.add("X-Trace", "abc");

// Manipulate body
request.body = JSON.stringify( { user:"admin" });
if(request.body.includes("admin") { ... }

// Read path
if (request.path.startsWith("/api/v1")) { ... }

// Edit query
request.query.addOrUpdate("lang","en")
request.query.remove("debug")
request.query.getQueryString()

// Read method
if (request.method == "GET") { ... }
警告: 由于脚本可能在每个请求中执行多次,建议使用 set 方法添加新标头,因为该方法会替换现有标头,从而避免重复。仅当需要多个名称相同但值不同的标头时,才使用 add 方法。如果已存在名称和值都相同的标头,add 方法将不会添加新标头。请注意避免重复标头,因为这可能会导致错误。
响应对象(响应上下文之后)- 完整 API
// Examples:
// Handle headers
response.headers.set("Authorization", "Bearer");
response.headers.add("X-Trace", "abc");

// Read body (since it's read only)
var j = JSON.parse(response.body);

// Read path
if (response.path.startsWith("/api/v1")) { ... }

// Read response
if (response.status.statusCode == 200) { ... }

// Read parent request
if (response.request.path.startsWith("/api/v1")) { ... }
参数上下文 - 完整 API
// Update the result back to AppScan
setResultValue("ABC-" + Date.now());

环境数据

短期数据仅在任务结束前可用 - 任务结束的场景包括暂停、停止或完成扫描,以及登录或结束手动测试。每次启动新扫描或重新扫描时,短期数据都会被清除。

  1. 全局变量: 全局变量数据是一种键-值数据结构,旨在存储用户特定的数据对。键是唯一的(意味着没有两个元素可以具有相同的键,尽管多个元素可以共享相同的值),这对于在运行中的脚本之间共享数据非常有用。
  2. 扫描状态:包含当前扫描的相关信息。
    // Check the status of the scan
    environment.status.isInExplore();
    environment.status.isInTest();

日志记录 - 完整 API

在脚本中启用日志记录功能以帮助进行调试和故障诊断。此环境中不支持标准 console.log 命令。相反,应改用 log 函数并传入带引号的消息,例如:log("Your message here")。如果在引擎操作期间发生与 JavaScript 语法或用法无关的运行时错误,则会将错误记录在引擎跟踪日志中,以便进一步分析。

日志将写入 AppScan 日志文件夹中的 UserScript.log 文件。

缺省情况下,该文件夹为 [UserDataFolder]\AppData\Roaming\HCL\AppScan Standard\logs

约束和执行限制

  • 受限函数eval, Function (constructor), setTimeout, setlnterval。如果使用了其中任何一个函数,脚本将失败。
  • 限制:最长执行时间:5 秒;内存限制:64 MB。超过这些限制会导致脚本失败。

    失败时扫描将暂停,详细信息将记录在 UserScript.log 中。

支持的 JavaScript 版本

ECMAScript ES6 (2015) 至 ES15 (2024),例外:
  • ES6:生成器、尾调用
  • ES8:共享内存和原子操作
  • ES15:Atomics.waitAsync、正则表达式标志 \v

自动完成中可用的对象

以下对象会显示在脚本编辑器的自动完成建议中,便于查找和使用:

  • Keyword
  • Array
  • Date
  • Object
  • String
  • RegExp
  • JSON
  • Map
  • Math
受支持但不在自动完成中

脚本引擎完全支持以下对象,但它们不会显示在自动完成列表中。您仍然可以通过在编辑器中直接键入其名称来使用它们:

  • ArrayBuffer
  • BigInt
  • Boolean
  • DataView
  • Error
  • Iterator
  • Number
  • Promise
  • Proxy
  • Reflect
  • SharedArrayBuffer
  • Symbol
  • TypedArray

  • WeakMap

  • WeakRef

  • WeakSet

    注: “不在自动完成中”表示编辑器不会自动建议这些项目;但是,这些对象的实现方式和行为与它们在标准 JavaScript 中完全相同。

故障诊断

常见问题和解决方案:
  1. 脚本失败:检查 logs 文件夹(缺省:[UserDataFolder]\AppData\Roaming\HCL\AppScan Standard\logs),以了解有关错误的详细信息。如果遇到未经处理的异常,请将代码封装在 try...catch 块中,以便正常处理。
  2. 脚本未执行:确保已在配置设置中启用脚本。

    如果问题与登录/授权相关,请考虑从配置 → 高级配置 → 安全包顺序中移除协商值,以防止 AppScan 自动处理这些身份验证。

请求完整 API

1. 请求对象 - 属性
成员 类型 描述 示例
headers 对象 一个封装当前 HTTP 请求标头的对象,提供动态添加、更新、检索或移除标头的方法,可实现对请求元数据的精准控制。 request.headers.set("Authorization","Bearer ...")
body 字符串 当前 HTTP 请求的主体。 request.body = JSON.stringify({ user:"jsmith" })
path 字符串

只读🔒

当前 HTTP 请求的相对路径。

if (request.path.startsWith("/api/v1/")) { ... }
query 对象 包含多种方法的对象,可用于在当前 HTTP 请求中添加、更新或移除查询参数,支持对查询字符串进行动态操控。 request.query.addOrUpdate("lang","en") request.query.remove("debug")
method 字符串

只读🔒

当前 HTTP 请求所使用的方法。

if (request.method == "GET") {{ ... }}
2. 查询对象 - 方法
成员 类型 描述 示例

addOrUpdate

(name: string,value: string)

将新值添加到查询字符串或更新现有值。 request.query.addOrUpdate("appscan","appscanvalue")
getQueryString() 字符串

返回当前 HTTP 请求的完整查询字符串,包括所有参数及其值。

request.query.getQueryString()

contains

(name: string)

布尔值

检查当前 HTTP 请求中是否存在特定查询参数。

如果参数存在,则返回 true,否则返回 false

request.query.contains("lang")

remove

(name: string)

布尔值

从当前 HTTP 请求中移除特定查询参数。

如果参数已移除,则返回 true,否则返回 false

request.query.remove("lang")
clear()

从当前 HTTP 请求中移除所有查询参数。

request.query.clear()

响应完整 API

3. 响应 objectStatus 对象
成员 类型 描述 示例
headers 对象 一个封装当前 HTTP 响应标头的对象,提供动态添加、更新、检索或移除标头的方法,可实现对响应元数据的精准控制。 response.headers.set("Authorization","Bearer ...")
body 字符串 当前 HTTP 响应的主体。 response.body = JSON.stringify({ user:"jsmith" })
path 字符串

只读🔒

当前 HTTP 响应的相对路径。

if (response.path.startsWith("/api/v1/")) { ... }
状态 对象 表示当前 HTTP 响应状态的对象,包括状态代码和状态消息属性。 if (response.status.code == 200) { ... }
request 对象 表示启动响应的请求的对象。请求数据为只读,包括在前面描述的请求对象中定义的所有属性。 if (response.request.path) { ... }
4. 属性
成员 类型 描述 示例
statusCode 数字

只读🔒

当前响应的 HTTP 状态代码。

response.status.statusCode = 200
statusDescription 字符串

只读🔒

当前响应的 HTTP 状态消息。

response.status.statusDescription = "OK"
statusLine 字符串

只读🔒

当前响应的 HTTP 状态行。

response.status.statusLine = "HTTP/1.1 200 OK"

参数完整 API

5. 参数对象
成员 类型 描述 示例

setResultValue

(value: string)

设置当前参数上下文脚本的结果值。 setResultValue("ABC-" + Date.now())

通用对象

6. 标头对象 - 属性
成员 类型 描述 示例
items 数组

只读🔒

当前 HTTP 请求中所有标头的数组。

if (request.headers.items.length > 0) { ... }

if (response.headers.items.length > 0) { ... }

7. 标头对象 - 方法
成员 类型 描述 示例
add

(name: string,value: string)

向当前 HTTP 请求添加新标头。如果已存在具有相同名称和值的标头,则不会再次添加。

request.headers.add("Authorization", "MyCoolApiKey")

response.headers.add("Authorization",MyCoolApiKey")

set

(name: string,value: string)

设置当前 HTTP 请求的标头。如果标头已存在,则替换其值。

request.headers.set("Authorization", "SecondApiKey")

response.headers.set("Authorization","SecondApiKey")

remove

(name: string)

从当前 HTTP 请求中移除标头。 request.headers.remove("Authorization")response.headers.remove("Authorization")
[name: string] 字符串 按名称检索特定标头的值。如果标头不存在,则返回 null。

request.headers['Authorization'] = "myapikey3"

response.headers['Authorization'] = "myapikey3"

环境数据

8. 环境对象 - 属性
成员 类型 描述 示例
globalVariables 对象 用于存储用户特定数据对的键值数据结构。键必须是唯一的,确保没有重复项,而多个键可以使用相同的值。此结构有助于在运行的脚本之间共享数据。 environment.globalVariables.addOrUpdate(key, value)
状态 对象 有关当前扫描状态的信息。 environment.status.isInExplore()
9. 全局变量对象 - 方法
成员 类型 描述 示例

addOrUpdate

(key: string,value:

string)

向全局变量添加新的键值对,或更新现有键的值。 environment.globalVariables.addOrUpdate("mycoolkey", "testValue")

remove

(key: string)

布尔值 从全局变量中移除键值对。如果找到并移除了键,则返回 true,否则返回 false。 environment.globalVariables.remove("mycoolkey")
get

(key: string)

字符串 从全局变量检索特定键的值。如果键不存在,则返回空字符串。 environment.globalVariables.get("mycoolkey")
10. 状态对象 - 方法
方法 类型 描述 示例
isInExplore() 布尔值 检查扫描当前是否处于探索阶段。 environment.status.isInExplore()
isInTest() 布尔值 检查扫描当前是否处于测试阶段。 environment.status.isInTest()

日志记录

11. 方法
成员 类型 描述 示例
log(message: string) log(message: string, source: string) 添加具有指定消息和可选来源的新日志条目。 log("This is a log message")

log("This is a log message", "mySource")