自訂 Script

自訂 Script 可讓您進行更動態化的 DAST 掃描。您可以在掃描期間新增 JavaScript,以在要求送出至伺服器之前或收到回應之後操作 HTTP 要求與回應。

概觀

自訂 Script 會在 AppScan 內執行,並操作記憶體中的要求與回應模型,讓您能以程式化方式調整要求與檢查回應,而無需建立擴充功能或使用 Proxy。

概覽

要求前:在要求送出至您的應用程式之前執行 Script,以修改 HTTP 要求(新增或移除標頭、加密資料、處理記號等)。

回應後:在收到應用程式回應後處理 HTTP 回應(解密內容、擷取值等)。

參數:僅使用 JavaScript 產生多步驟作業表單填寫器的值。

睿智運用此功能,便可提升掃描效率和效益。在上線部署前,先於控制環境中測試 Script,以確保如預期運作。

範例範本路徑 C:\Program Files (x86)\HCL\AppScan Standard\Templates.

自訂 Script 範例:GitHub 儲存庫

新增自訂 Script

依下列步驟,將自訂 JavaScript 新增至 DAST 掃描:
  1. 選擇配置設定的「進階」區段下的「自訂 Script」。
  2. 按一下 + 新增按鈕。
  3. 視需要,編輯預先填入的「名稱」欄位。
  4. 為 Script 選擇合適的「執行環境定義」::
    1. 要求前」適用於在傳送要求前執行的 Script。
    2. 回應後」適用於在接收回應後執行的 Script。
    3. 參數」適用於需要多步驟或填寫表單的作業。您可以搭配 setResultValue 方法使用此環境定義,讓 Script 傳回結果至 AppScan
  5. JavaScript 欄位(程式碼編輯器)輸入 JavaScript,並在 AppScan 環境中使用 Ctrl+Space 以取得自動完成的建議。
  6. 按一下新增以儲存 Script。Script 會新增至清單且預設為啟用。
  7. 您可利用啟用勾選框,從自訂 Script 清單選擇啟用或停用 Script。
  8. 使用「啟用」勾選框管理 Script,或在 Script 清單中編輯、刪除或重新排序。 「選項」功能表
  9. 按一下開始完整掃描以執行掃描。
  10. 結果:每項要求或回應都會執行啟用的 Script。您可以從資料 > 要求標籤 > 要求/回應,檢視 Script 的詳細資料。

    驗證 Script 的語法是否準確,以及是否與 AppScan 支援的 JavaScript 版本相容。如需詳細指南,請參閱 JavaScript 程式碼。

    註:
    • 自訂 Script 會儲存在掃描配置檔中,並可與範本一併匯出。它們不會包含在掃描結果中。
    • 自訂 Script 在匯出至範本時會預設加密。若要停用加密,請在掃描配置中將「一般」>「加密機密資料」設定為 False

啟用自訂 Script

自訂 Script 預設為已啟用,並會在錄製與掃描期間隨每個 HTTP 要求與回應執行。

若要停用 Script 執行,請前往工具 > 選項 > 進階標籤,並將 CustomScripts.Enable 設定為「False」。

執行內容

要求前

目的:修改傳出的 HTTP 要求。

資料:僅可使用要求與環境物件,其他物件將為空值。
// Attach Authorization from stored global variable
var token = environment.globalVariables.get("Authorization");
if (token) request.headers.set("Authorization", token);

回應後

用途:檢查回應並保留值。

資料:僅可使用回應與環境資料物件。其他物件將為空值。
// 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); }

參數

目的:產生多步驟作業表單填寫器的值。
  • 此內容沒有可用的資料物件(要求或回應);若使用,將傳回空值。
  • 使用 setResultValue(value) 傳回值。

在表單填寫器中,當您選取值來源 > 自訂 Script時,清單僅會顯示執行內容為參數的 Script。

在多步驟作業中,您也可以使用參數 Script 提供動態值。
// 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") { ... }
注意: 由於 Script 可能依要求而執行多次,建議使用 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. 廣域變數:廣域變數資料具有金鑰-值資料結構,專為儲存使用者特有的資料配對而設計。金鑰為獨一無二(表示兩個元素不會有相同金鑰,但多重元素可共用相同值),這一點對於在執行 Script 之間共用資料而言非常實用。
  2. 掃描狀態:包含現行掃描的相關資訊。
    // Check the status of the scan
    environment.status.isInExplore();
    environment.status.isInTest();

記錄-完整 API

在 Script 中啟用記錄功能,以協助除錯與疑難排解。此環境不支援標準的 console.log 指令。請改用 log 函式並搭配引號訊息,例如:log("Your message here")。若在執行期間發生與 JavaScript 語法或使用無關的執行階段錯誤,該錯誤將記錄於引擎追蹤記錄中以供進一步分析。

記錄會寫入 AppScan 記錄資料夾中的 UserScript.log 檔案。

預設資料夾位置為 [UserDataFolder]\AppData\Roaming\HCL\AppScan Standard\logs

限制與執行上限

  • 受限函式eval, Function (constructor), setTimeout, setlnterval。若使用其中任一函式,Script 將執行失敗。
  • 限制:執行時間上限:5 秒;記憶體限制:64 MB。超出此限制都會導致 Script 失敗。

    當發生錯誤時,掃描將暫停,詳細資訊會記錄在 UserScript.log 中。

支援的 JavaScript 版本

支援 ECMAScript ES6 (2015) 至 ES15 (2024),但以下功能除外:
  • ES6:產生器、尾呼叫
  • ES8:共享記憶體與 Atomic
  • ES15:Atomics.waitAsync、正規表示式旗標 \v

自動完成中可用的物件

這些物件會出現在 Script 編輯器的自動完成建議中,方便搜尋與使用:

  • 關鍵字
  • 陣列
  • 日期
  • 物件
  • 字串
  • RegExp
  • JSON
  • 對映
  • Math
受支援但不在自動完成中

這些物件已完全受 Script 引擎支援,但不會出現在自動完成清單中。您仍可在編輯器中直接輸入名稱來使用它們:

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

  • WeakMap

  • WeakRef

  • WeakSet

    註: 「不在自動完成中」表示編輯器不會自動建議這些項目,但這些物件仍已實作,且行為與標準 JavaScript 完全相同。

疑難排解

常見問題與解決方案:
  1. Script 失敗:請檢查日誌資料夾(預設位置:[UserDataFolder]\AppData\Roaming\HCL\AppScan Standard\logs)以取得錯誤詳細資訊。若遇到未處理的例外,請使用 try...catch 區塊包覆程式碼,以妥善處理錯誤。
  2. Script 未執行:請確認已在配置設定中啟用 Script。

    若問題與登入或授權相關,請考慮從配置 → 進階配置 → 安全性套件順序中移除 negotiate 值,防止 AppScan 自動處理這些鑑別。

要求完整 API

1. 要求物件-屬性
成員 類型 說明 範例
標頭 物件 封裝目前 HTTP 要求標頭的物件,提供新增、更新、擷取或移除標頭的方法,讓您能動態且精確地控制要求的中繼資料。 request.headers.set("Authorization","Bearer ...")
主體 字串 目前 HTTP 要求的本文。 request.body = JSON.stringify({ user:"jsmith" })
路徑 字串

唯讀🔒

目前 HTTP 要求的相對路徑。

if (request.path.startsWith("/api/v1/")) { ... }
查詢 物件 包含多種方法可在目前的 HTTP 要求中新增、更新或移除查詢參數的物件,讓您能動態操作查詢字串。 request.query.addOrUpdate("lang","en") request.query.remove("debug")
方法 字串

唯讀🔒

目前 HTTP 要求的方法。

if (request.method == "GET") {{ ... }}
2. 查詢物件-方法
成員 類型 說明 範例

addOrUpdate

(name: string, value: string)

void 在查詢字串中新增值或更新現有值。 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() void

從目前的 HTTP 要求中移除所有查詢參數。

request.query.clear()

回應完整 API

3. 回應 objectStatus 物件
成員 類型 說明 範例
標頭 物件 封裝目前 HTTP 回應標頭的物件,提供新增、更新、擷取或移除標頭的方法,讓您能動態且精確地控制回應的中繼資料。 response.headers.set("Authorization","Bearer ...")
主體 字串 目前 HTTP 回應的本文。 response.body = JSON.stringify({ user:"jsmith" })
路徑 字串

唯讀🔒

目前 HTTP 回應的相對路徑。

if (response.path.startsWith("/api/v1/")) { ... }
狀態 物件 表示目前 HTTP 回應狀態的物件,包含狀態碼與狀態訊息等屬性。 if (response.status.code == 200) { ... }
要求 物件 表示觸發此回應的要求物件。要求資料為唯讀,且包含先前所述要求物件中定義的所有屬性。 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)

void 設定目前的參數內容 Script 結果值。 setResultValue("ABC-" + Date.now())

常見物件

6. 標頭物件-屬性
成員 類型 說明 範例
項目 陣列

唯讀🔒

目前 HTTP 要求中所有標頭的陣列。

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

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

7. 標頭物件-方法
成員 類型 說明 範例
add

(name: string, value: string)

void

在目前的 HTTP 要求中新增一個標頭。若已存在相同名稱與值的標頭,則不會再次新增。

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

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

set

(name: string, value: string)

void 設定目前的 HTTP 要求中的一個標頭。若該標頭已存在,將以新值取代舊值。

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

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

remove

(name: string)

void 從目前的 HTTP 要求中移除一個標頭。 request.headers.remove("Authorization")response.headers.remove("Authorization")
[name: string] 字串 依標頭名稱擷取特定標頭的值。若該標頭不存在,則傳回空值。

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

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

環境資料

8. 環境物件-屬性
成員 類型 說明 範例
globalVariables 物件 用於儲存使用者特定資料配對的鍵值資料結構。鍵必須唯一,確保不重複,而值可在多個鍵之間共用。此結構可促進執行中 Script 之間的資料共用。 environment.globalVariables.addOrUpdate(key, value)
狀態 物件 關於目前掃描狀態的資訊。 environment.status.isInExplore()
9. 廣域變數物件-方法
成員 類型 說明 範例

addOrUpdate

(key: string, value:

string)

void 新增一組鍵值配對至廣域變數,或更新現有鍵的值。 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) void 新增一筆包含指定訊息與可選來源的日誌項目。 log("This is a log message")

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