加解密模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 假设响应加密方式为 aes-cbc
// 秘钥(key)为1234567890123456,向量(iv)为1234567890123456
// 例如响应为{"encrypted":"nwvjULjLOqzUFt9nQt+gVg==", "key":"1234567890123456", "iv":"1234567890123456"}
key = "1234567890123456"
iv = "1234567890123456"
decrypt = func(rsp) {
body = poc.GetHTTPPacketBody(rsp)
data = json.loads(body)
if "encrypted" in data {
encrypted = data.encrypted
encrypted = codec.DecodeBase64(encrypted)~ // 一般会将密文base64一次,所以要先解码
decrypted = codec.AESCBCDecrypt(key, encrypted, iv)~
if decrypted != "" {
rsp = poc.ReplaceHTTPPacketBody(rsp, decrypted) // 替换body
}
}
return rsp
}

encrypt = func(rsp) {
body = poc.GetHTTPPacketBody(rsp)
encrypted = codec.AESCBCEncrypt(key, body, iv)~
encrypted = codec.EncodeBase64(encrypted)
encryptedBody = omap({"encrypted":encrypted, "key":key, "iv":iv})
rsp = poc.ReplaceHTTPPacketBody(rsp, json.dumps(encryptedBody)) // 替换body
return rsp
}

// hijackHTTPResponseEx 是hijackHTTPResponse的扩展,能够获取到响应对应的请求,会在过滤后的响应到达Yakit MITM前被调用,可以通过该函数提前将响应修改或丢弃
// !!! 通常实现hijackHTTPResponse 或 hijackHTTPResponseEx 其中一个函数即可
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// forward(req) 提交修改后的响应,如果未被调用,则使用原始的响应
// drop() 丢弃响应
hijackHTTPResponseEx = func(isHttps /*bool*/, url /*string*/, req/*[]byte*/, rsp /*[]byte*/, forward /*func(modifiedResponse []byte)*/, drop /*func()*/) {
// 这里要对劫持的响应做解密再返回给Yakit
rsp = decrypt(rsp)
forward(rsp)
}

// afterRequest 会在响应到达客户端之前被调用,可以通过该函数对响应做最后一次修改
// isHttps 请求是否为https请求
// oreq 原始请求
// req hijackRequest修改后的请求
// orsp 原始响应
// rsp hijackHTTPResponse/hijackHTTPResponseEx修改后的响应
// 返回值: 修改后的响应,如果没有返回值则使用hijackHTTPResponse/hijackHTTPResponseEx修改后的响应
afterRequest = func(ishttps, oreq/*原始请求*/ ,req/*hiajck修改之后的请求*/ ,orsp/*原始响应*/ ,rsp/*hijack修改后的响应*/){
// Yakit查看之后需要再加密回去
// !!! 也可以不加密回去,这样客户端拿到的也是解密后的数据
// 如果这里不加密,那么后续hijackSaveHTTPFlow也不需要再解密
rsp = encrypt(rsp)
return rsp
}

// hijackSaveHTTPFlow 会在流量被存储到数据库前被调用,可以通过该函数对入库前的流量进行修改,例如修改请求/响应,添加tag/染色等
// flow 流量结构体,可以通过鼠标悬浮提示查看其拥有的字段并对其进行修改
// modify(modified) 提交修改后的流量结构体,如果未被调用,则使用原始的流量结构体
// drop() 丢弃流量
hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) {
// flow.Request 转义后的请求
// flow.Response 转义后的响应
// 对于转义后的请求和响应,需要通过以下方式拿到原始的请求/响应
// req = str.Unquote(flow.Request)~
// rsp = str.Unquote(flow.Response)~
// 对于修改后的请求和响应,需要通过以下方式再将其转义回去
// flow.Request = str.Quote(req)
// flow.Response = str.Quote(rsp)
// flow.AddTag("tag") // 添加tag
// flow.Red() // 染红色
// 需要在流量中再将数据解密
rsp = str.Unquote(flow.Response)~
rsp = decrypt(rsp)
flow.Response = str.Quote(rsp)
modify(flow)
}

mockhttp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// mockHTTPRequest 会在请求即将发往真实服务器之前被调用。
// 你可以通过调用 mockResponse(fakeResponse) 来伪造一个响应直接返回给客户端,从而阻止原始请求被发送。
// isHttps (bool): 请求是否为HTTPS协议。
// url (string): 请求的完整URL。
// req ([]byte): 完整的原始HTTP请求报文。
// mockResponse func(fakeResponse string): 回调函数,用于注入虚假的响应。
mockHTTPRequest = func(isHttps, url, req, mockResponse) {
// 场景1:Mock一个成功的JSON响应 (例如:获取用户信息)
// 适用于测试前端在拿到正确数据后,UI是否能正常渲染。
if str.Contains(url, "/api/user/profile") {
yakit.Info("[MOCK] 拦截到用户信息请求,返回成功的模拟数据: " + url)
// 构造一个合法的 HTTP 响应报文
// 关键点:
// 1. 状态行: "HTTP/1.1 200 OK"
// 2. 响应头: 至少要有 Content-Type,跨域的请求需要 Access-Control-Allow-Origin
// 3. 空行: 响应头和响应体之间必须有一个空行 `\r\n\r\n`
// 4. 响应体: JSON 字符串
successResponse := "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n\r\n" +
`{"ok":true, "code": 200, "data": {"username": "mock-user", "email": "mock@example.com", "level": 99}}`

// 调用 mockResponse 将伪造的响应返回给客户端
mockResponse(successResponse)
}
// 场景2:Mock一个失败的响应 (例如:服务端错误)
// 适用于测试前端在遇到服务器5xx错误时,是否能优雅地处理并给出提示。
if str.Contains(url, "/api/submit/order") {
yakit.Info("[MOCK] 拦截到订单提交请求,返回服务器错误: " + url)

errorResponse := "HTTP/1.1 503 Service Unavailable\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n\r\n" +
`{"ok":false, "message": "服务暂时不可用,请稍后再试"}`

mockResponse(errorResponse)
}
// 场景3:根据请求体内容进行复杂判断和Mock (例如:阻止危险操作)
// 适用于测试前端对特定输入的处理,或防止测试时产生垃圾数据。
if str.Contains(url, "/api/data/delete") && str.Contains(string(req), `"is_production":true`) {
yakit.Info("[MOCK] 检测到危险的删除操作,已拦截并返回'禁止操作'响应: " + url)

// 返回一个 403 Forbidden 响应
forbiddenResponse := "HTTP/1.1 403 Forbidden\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n\r\n" +
`{"ok":false, "message": "模拟环境禁止删除线上数据!"}`
mockResponse(forbiddenResponse)
}
// 如果以上 if 条件都没有命中,函数会默认结束,Yakit将正常处理该请求(即将其发往后端服务器)。
}

替换请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 修改课程进度参数的热加载函数
modifyCourseProgress = func(req) {
// 检查是否为目标URL
if !str.Contains(string(req), "/p/course/services/member/study/progress") {
return req
}

println("[DEBUG] ✓ 匹配到目标URL,开始修改进度参数")

// 使用fuzz模块构建可修改的请求对象
freq = fuzz.MustHTTPRequest(req)

println(freq)

// 检查并修改playProgress参数 - 使用GetPostQueryValue
// if freq.GetPostQueryValue("playProgress") != "" {
// originalValue = freq.GetPostQueryValue("playProgress")
// println("[DEBUG] 修改playProgress参数: ", originalValue, " -> 1667")
// freq = freq.FuzzPostParams("playProgress", originalValue*16)
// }

// // 检查并修改clockInDot参数 - 使用GetPostQueryValue
// if freq.GetPostQueryValue("clockInDot") != "" {
// originalValue = freq.GetPostQueryValue("clockInDot")
// println("[DEBUG] 修改clockInDot参数: ", originalValue, " -> 1667")
// freq = freq.FuzzPostParams("clockInDot", originalValue*16)
// }

// // 返回修改后的请求字节流
// modifiedReq = freq.FirstHTTPRequestBytes()
// println("[DEBUG] ✓ 参数修改完成",modifiedReq)
// return modifiedReq
return req
}

// 在请求到达服务器前进行修改
beforeRequest = func(ishttps, oreq, req) {
return modifyCourseProgress(req)
}

// 使用hijackHTTPRequest进行更精确的劫持
hijackHTTPRequest = func(isHttps, url, req, forward, drop) {
if str.Contains(url, "/p/course/services/member/study/progress") {
println("[DEBUG] 在hijackHTTPRequest中处理目标URL")
modifiedReq = modifyCourseProgress(req)
forward(modifiedReq)
return
}
forward(req)
}

替换结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// 请求 -> hijackHTTPRequest -> 前端劫持 -> beforeRequest -> 服务器响应 ->  hijackResponse -> 后端劫持 -> afterRequest -> 客户端看到的响应 -> hijackSaveHTTPFlow

// mirrorHTTPFlow 会镜像所有的流量到这里,包括被过滤器过滤的请求
// !!! 一般插件不要实现这个接口
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// body 响应体
mirrorHTTPFlow = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {

}

// mirrorFilteredHTTPFlow 会镜像过滤后的流量到这里
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// body 响应体
mirrorFilteredHTTPFlow = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {

}

// mirrorNewWebsite 会镜像过滤后的流量到这里,每个网站只会触发一次
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// body 响应体
mirrorNewWebsite = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {

}

// mirrorNewWebsitePath 会镜像过滤后的流量到这里,每个网站的相同路径只会触发一次
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// body 响应体
mirrorNewWebsitePath = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {

}

// mirrorNewWebsitePathParams 会镜像过滤后的流量到这里,每个网站的参数相同的请求只会触发一次
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// body 响应体
mirrorNewWebsitePathParams = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {

}

// hijackHTTPRequest 会在过滤后的请求到达Yakit MITM前被调用,可以通过该函数提前将请求修改或丢弃
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// forward(req) 提交修改后的请求,如果未被调用,则使用原始的请求
// drop() 丢弃请求
hijackHTTPRequest = func(isHttps, url, req, forward /*func(modifiedRequest []byte)*/, drop /*func()*/) {
// Example:
// if str.Contains(string(req), "/should_modify") {
// modified = str.ReplaceAll(string(req), "/should_modify", "/modified")
// forward(poc.FixHTTPRequest(modified))
// }

// if str.Contains(string(req), "/drop") {
// drop()
// }
}


// hijackHTTPResponse 会在过滤后的响应到达Yakit MITM前被调用,可以通过该函数提前将响应修改或丢弃
// isHttps 请求是否为https请求
// url 网站URL
// rsp 响应
// forward(req) 提交修改后的响应,如果未被调用,则使用原始的响应
// drop() 丢弃响应
hijackHTTPResponse = func(isHttps /*bool*/, url /*string*/, rsp /*[]byte*/, forward /*func(modifiedResponse []byte)*/, drop /*func()*/) {
// Example:
// if str.Contains(string(rsp), "凝聚磅礴的中国文学力量") {
// modified = poc.FixHTTPResponse(str.ReplaceAll(rsp, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA"))
// forward(modified)
// }
// 将响应体转换为字符串
rspStr = string(rsp)

// 检查是否为目标URL
targetURL = "/h5/api/mobile/checkCode?eventVisitorId=RE2_6e4addb7f0545a7679bcb40a7a3568ee1765183929556GM1GH530&eventTraceId=487659ece23c9eab"

// if str.Contains(url, targetURL) {
// println("[DEBUG] ✓ 匹配到目标URL,开始替换响应")

// // 固定的响应内容
// fixedResponse = `{"data":{"key":"693fe38971374819aa07cf5e8de7c91c","img":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAAoCAIAAAC6iKlyAAABTElEQVR42u3aOw7CMAwG4F6JjVuxciRWRrZKnIUDlBOwMFSQ2PErbdzfytqq/WpZsZtpRnSJCQSABjQC0IAGNALQgP6G5bHY1/15Vaz/h7mdXsUleqP3ci4uQFPEIu4asRf38NAtyrR1C/HG0O2fwX43FpotJi3K9MfID03XDeJC1tq3EKeCll5LQHdTHgOa3W/ooHsqDwBtVAZ0j6IB6O3TeQ09fAvuC90nnSN6wr1Dx+VyETquVdk1tKJoiBrCH8TotnAMaEUKSxvCImX+FlxXNESDjkbEzND2nUbNXSfoZb1raPsz1KxFdgmhfZUJ60NDexWNlq0eoP2VAd0pnWvNyxGhQ5VZaJYvJ3SosgI6zz7aPte3D/DYv4XDQzsOQqU9C+uYatahO2vQ/7jB8NM7O/TscYAmWjkJtKhWiLgdNQKhEYAGNKARxvgACVtTFUrORRAAAAAASUVORK5CYII="},"code":"success","msg":null}`

// // 修复HTTP响应格式
// modifiedRsp = poc.FixHTTPResponse(fixedResponse)
// forward(modifiedRsp)
// println("[DEBUG] ✓ 响应已替换为固定内容")
// return
// }

// if str.Contains(rspStr, "手机号不在景德镇") {
// println("进来了吗")
// // 替换data对象下的字段
// modifiedStr = str.Replace(rspStr, `"code":"35"`, `"code":"0"`,200)
// println(modifiedStr)
// modifiedStr = str.Replace(modifiedStr, `"message":"手机号不在景德镇"`,`"message":"恭喜您,中奖啦~"`,500)
// modifiedStr = str.Replace(modifiedStr, `"errorKey":"actUserPhoneCityLimit"`,`"errorKey":"actUserPhoneCityLimit"`,500)

// // 替换checkResultList中特定对象的字段
// // modifiedStr = str.ReplaceAll(modifiedStr, `"errorCode": "35"`, `"errorCode": "0"`)
// // modifiedStr = str.ReplaceAll(modifiedStr, `"errorMessage": "手机号不在景德镇"`, `"errorMessage": "恭喜您,中奖啦~"`)

// modifiedRsp = poc.FixHTTPResponse(modifiedStr)
// forward(modifiedRsp)
// return
// }

// 检查是否包含目标错误信息
// if str.Contains(rspStr, `1015`) {
// // 替换响应体内容
// modifiedStr = str.ReplaceAll(rspStr, `1015`, `200`).ReplaceAll(`500`, `200`)
// // modifiedStr = str.ReplaceAll(rspStr, `1015`, `200`)
// // 修复HTTP响应格式并转发
// modifiedRsp = poc.FixHTTPResponse(modifiedStr)
// forward(modifiedRsp)
// return
// }
// 检查是否为JSONP格式响应
// 检查JSONP格式和locations字段
// 检查是否为JSONP格式响应
// if str.Contains(rspStr, "jsonp_") && str.Contains(rspStr, "locations") {
// // 先尝试提取原坐标值
// parts = str.Split(rspStr, `"locations":"`)
// if len(parts) > 1 {
// subParts = str.Split(parts[1], `"`)
// if len(subParts) > 0 {
// oldValue = subParts[0]

// // 使用ReplaceAll替换坐标值
// modifiedStr = str.ReplaceAll(rspStr, `"locations":"` + oldValue + `"`, `"locations":"117.146155108562,28.981328809593"`)

// modifiedRsp = poc.FixHTTPResponse(modifiedStr)
// forward(modifiedRsp)
// return
// }
// }
// }


// 检查是否包含目标错误信息
// if str.Contains(rspStr, `"code":"1002"`) {
// // 替换响应体内容
// modifiedStr = str.ReplaceAll(rspStr, `"code":"1002","retCode":"500"`, `"code":"200","retCode":"200"`)

// // 修复HTTP响应格式并转发
// modifiedRsp = poc.FixHTTPResponse(modifiedStr)
// forward(modifiedRsp)
// return
// }

// 如果不匹配,使用原始响应
forward(rsp)
}


// hijackHTTPResponseEx 是hijackHTTPResponse的扩展,能够获取到响应对应的请求,会在过滤后的响应到达Yakit MITM前被调用,可以通过该函数提前将响应修改或丢弃
// !!! 通常实现hijackHTTPResponse 或 hijackHTTPResponseEx 其中一个函数即可
// isHttps 请求是否为https请求
// url 网站URL
// req 请求
// rsp 响应
// forward(req) 提交修改后的响应,如果未被调用,则使用原始的响应
// drop() 丢弃响应
hijackHTTPResponseEx = func(isHttps /*bool*/, url /*string*/, req/*[]byte*/, rsp /*[]byte*/, forward /*func(modifiedResponse []byte)*/, drop /*func()*/) {
// Example:
// if str.Contains(string(rsp), "凝聚磅礴的中国文学力量") {
// modified = poc.FixHTTPResponse(str.ReplaceAll(rsp, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA"))
// forward(modified)
// }
}

// beforeRequest 会在请求到达服务器之前被调用,可以通过该函数对请求做最后一次修改
// isHttps 请求是否为https请求
// oreq 原始请求
// req hijackRequest修改后的请求
// 返回值: 修改后的请求,如果没有返回值则使用hijackRequest修改后的请求
beforeRequest = func(ishttps /*bool*/, oreq /*[]byte*/, req/*[]byte*/){
// Example:
// if str.Contains(string(req), "凝聚磅礴的中国文学力量") {
// modified = poc.FixHTTPRequest(str.ReplaceAll(req, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA"))
// return []byte(modified)
// }
}

// afterRequest 会在响应到达客户端之前被调用,可以通过该函数对响应做最后一次修改
// isHttps 请求是否为https请求
// oreq 原始请求
// req hijackRequest修改后的请求
// orsp 原始响应
// rsp hijackHTTPResponse/hijackHTTPResponseEx修改后的响应
// 返回值: 修改后的响应,如果没有返回值则使用hijackHTTPResponse/hijackHTTPResponseEx修改后的响应
afterRequest = func(ishttps, oreq/*原始请求*/ ,req/*hiajck修改之后的请求*/ ,orsp/*原始响应*/ ,rsp/*hijack修改后的响应*/){
// Example:
// if str.Contains(string(rsp), "凝聚磅礴的中国文学力量") {
// modified = poc.FixHTTPRequest(str.ReplaceAll(rsp, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA"))
// return []byte(modified)
// }
}

// hijackSaveHTTPFlow 会在流量被存储到数据库前被调用,可以通过该函数对入库前的流量进行修改,例如修改请求/响应,添加tag/染色等
// flow 流量结构体,可以通过鼠标悬浮提示查看其拥有的字段并对其进行修改
// modify(modified) 提交修改后的流量结构体,如果未被调用,则使用原始的流量结构体
// drop() 丢弃流量
hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) {
// flow.Request 转义后的请求
// flow.Response 转义后的响应
// 对于转义后的请求和响应,需要通过以下方式拿到原始的请求/响应
// req = str.Unquote(flow.Request)~
// rsp = str.Unquote(flow.Response)~
// 对于修改后的请求和响应,需要通过以下方式再将其转义回去
// flow.Request = str.Quote(req)
// flow.Response = str.Quote(rsp)
// flow.AddTag("tag") // 添加tag
// flow.Red() // 染红色
//
// Example:
// responseBytes, _ = codec.StrconvUnquote(flow.Response)
// if str.MatchAnyOfRegexp(responseBytes, "/admin/", "accessKey") {
// flow.Red();
// modify(flow)
// }
}


替换结果2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
TARGET_PATH = "/wsctrade/cartGoodstList.json"
TARGET_START_SOLD_TIME = 1774445201

rewriteCartGoodsListResponse = func(rsp) {
body = string(poc.GetHTTPPacketBody(rsp))
if body == "" {
return rsp
}

body = re.ReplaceAll(body, `"disable_select"\s*:\s*true`, `"disable_select": false`)
body = re.ReplaceAll(body, `"start_sold_time"\s*:\s*\d+`, str.f(`"start_sold_time": %d`, TARGET_START_SOLD_TIME))

return poc.ReplaceHTTPPacketBody(rsp, body)
}

beforeRequest = func(isHttps, oreq, req) {
if !str.Contains(string(req), TARGET_PATH) {
return req
}

println("[DEBUG] match target request, force identity encoding")
return poc.ReplaceHTTPPacketHeader(req, "Accept-Encoding", "identity")
}

hijackHTTPRequest = func(isHttps, url, req, forward, drop) {
if !str.Contains(url, TARGET_PATH) {
forward(req)
return
}

println("[DEBUG] hijackHTTPRequest match target URL")
forward(poc.ReplaceHTTPPacketHeader(req, "Accept-Encoding", "identity"))
}

hijackHTTPResponse = func(isHttps, url, rsp, forward, drop) {
if !str.Contains(url, TARGET_PATH) {
forward(rsp)
return
}

println("[DEBUG] hijackHTTPResponse match target URL, rewrite response body")
forward(rewriteCartGoodsListResponse(rsp))
}