定制脚本
定制脚本可以使您的 DAST 扫描更加灵活。您可以添加 JavaScript 以在扫描期间操控 HTTP 请求或响应。此操作可在请求发送到服务器之前或收到响应之后完成。
如何为 AppScan 启用定制脚本
缺省情况下启用定制脚本(CustomScripts.Enable 设置设定为 True)。
要禁用脚本,请执行以下操作:
- 转至工具 > 选项> 高级选项卡。
- 将 CustomScripts.Enable 设置设定为“False”。
如何为 DAST 扫描添加和启用定制脚本
- 导航到配置设置中的高级部分下的定制脚本。
- 单击 + 添加按钮。
- 名称字段预先填充了脚本创建的日期和时间。您可以根据需要更改名称。
- 在执行上下文下拉列表中,选择请求之前或响应之后,具体取决于您希望脚本运行的时间。
- 在 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}`) }
- 单击添加以保存您的脚本。该脚本将添加到列表中并缺省启用。Note: 在 DAST 扫描期间,您添加的脚本将针对每个 HTTP 请求或响应执行。合理使用此功能可以提高扫描的效率和效果。在实时扫描中部署脚本之前,请务必在受控环境中测试脚本,以避免出现任何意外行为或结果。
- 您可以使用启用复选框,从定制脚本列表中选择启用或禁用脚本。
- 您可以修改、删除或更改脚本的执行顺序。在脚本的右上角,单击
并选择如下选项:- 编辑
- 删除
- 上移
- 下移
- 单击开始全面扫描运行扫描。
- 结果:将针对每个请求或响应执行启用的脚本。您可以从数据 > 请求选项卡 > 请求/响应查看脚本的详细信息。Note: 当您导出模板中的定制脚本时,缺省情况下会对脚本进行加密。要手动禁用此加密,请从扫描配置设置中转到高级配置并设置常规:将加密敏感数据设为 False。
JavaScript 代码
上下文
请求:您可以在请求数据发送到服务器之前读取或修改相应数据。每个请求都由一个请求对象表示,该对象具有以下属性:- 标头:包含所有请求标头。除缺省标头外,您可以添加、删除或修改任何标头。
// 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"
缺省标头(不区分大小写):这些是只读的,无法修改。- 主机:识别请求将发送到的主机。
- 内容长度:计算请求的大小,并将该信息发送到服务器。
- 正文:包含有关请求正文的信息。正文为“字符串”类型。
// Access body request.body
// Example request.body = "{ "username":"user" }"
- 路径:保存有关请求所针对的端点的信息。端点格式不包括基本 URL(例如
/api/v1/test)。该属性是只读的;即使您在运行脚本之前尝试进行更改,它仍然会与首次设置时相同。// Access url path request.path
// Example if (request.path.startsWith("api/v1/test")) - 查询:保存当前请求的查询字符串和参数(如果有)。您可以在此对象中添加或删除查询参数。每个值都被视为单个对象,如“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
- 方法:请求的方法(例如 GET/POST/等)。该属性是只读的;即使您在运行脚本之前尝试进行更改,它仍然会与首次设置时相同。
// Access to the method request.method
// Example if (request.method == "GET")
- 正文:包含请求的响应正文。响应为字符串格式,如有必要,您需要手动进行转换(例如,转换为 JSON)。
// Access body response.body
// Example let json = JSON.parse(config.response)
- 路径:包含有关响应端点的信息。
// Access to the relative url response.path
// Example if (response.path.startsWith("api/v1/test")) - 状态:存储有关响应状态的详细信息。状态是一个具有以下属性的对象:
// 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
- 标头:包含所有响应标头。除缺省标头外,您可以添加、删除或修改任何标头。
// 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"
- 父项请求:包含在响应之前发送的请求的对象。请求数据是只读的。该请求具有先前定义的请求对象的所有属性。
// Example response.request.path //- access to the path from the request
环境数据
短期数据对象会存储重要信息,直到任务结束,例如当您暂停、停止或完成扫描、登录或运行手动测试时。
- 全局变量: 全局变量数据是一种键-值数据结构,旨在存储用户特定的数据对。键是唯一的(意味着没有两个元素可以具有相同的键,尽管多个元素可以共享相同的值),这对于在运行中的脚本之间共享数据非常有用。
每次完成新扫描或重新扫描后,系统都会擦除这些数据。
// 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)
- 扫描状态:包含当前扫描的相关信息。
// 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)
- 生成器
- 尾调用
- ES8 (2017)
- 共享内存和原子操作
- ES15 (2024)
- Atomics.waitAsync
- 正则表达式标志 \v