Skip to content

Commit a727590

Browse files
authored
Merge pull request #466 from psiinon/af-api
2 parents 882dbc0 + 0b5286f commit a727590

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

other/af-plans/ApiScanExample.yaml

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
---
2+
# This plan is the equivalent of the Packaged API scan https://www.zaproxy.org/docs/docker/api-scan/
3+
# The plan that will not do anything until you:
4+
# Set a "ZAP_TARGET" env var (or change the plan of course).
5+
# Define at least one graphql, openapi, or soap endpoint, then you can delete the API jobs that don't have one.
6+
env:
7+
contexts:
8+
- name: "Example"
9+
urls:
10+
- "${ZAP_TARGET}"
11+
includePaths: []
12+
excludePaths: []
13+
parameters:
14+
failOnError: true
15+
failOnWarning: false
16+
progressToStdout: true
17+
vars: {}
18+
jobs:
19+
- parameters:
20+
scanOnlyInScope: true
21+
enableTags: false
22+
rules: []
23+
name: "passiveScan-config"
24+
type: "passiveScan-config"
25+
26+
- type: script
27+
parameters:
28+
action: add
29+
type: httpsender
30+
engine: "ECMAScript : Graal.js"
31+
name: AlertOnHttpResponseCodeErrors.js
32+
inline: |
33+
var Pattern = Java.type("java.util.regex.Pattern")
34+
35+
pluginid = 100000 // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md
36+
37+
function sendingRequest(msg, initiator, helper) {
38+
// Nothing to do
39+
}
40+
41+
function responseReceived(msg, initiator, helper) {
42+
if (isGloballyExcluded(msg)) {
43+
// Not of interest.
44+
return
45+
}
46+
47+
var extensionAlert = control.getExtensionLoader().getExtension(org.zaproxy.zap.extension.alert.ExtensionAlert.NAME)
48+
if (extensionAlert != null) {
49+
var code = msg.getResponseHeader().getStatusCode()
50+
if (code < 400 || code >= 600) {
51+
// Do nothing
52+
} else {
53+
var risk = 0 // Info
54+
var title = "A Client Error response code was returned by the server"
55+
if (code >= 500) {
56+
// Server error
57+
risk = 1 // Low
58+
title = "A Server Error response code was returned by the server"
59+
}
60+
// CONFIDENCE_HIGH = 3 (we can be pretty sure we're right)
61+
var alert = new org.parosproxy.paros.core.scanner.Alert(pluginid, risk, 3, title)
62+
var ref = msg.getHistoryRef()
63+
if (ref != null && org.parosproxy.paros.model.HistoryReference.getTemporaryTypes().contains(
64+
java.lang.Integer.valueOf(ref.getHistoryType()))) {
65+
// Dont use temporary types as they will get deleted
66+
ref = null
67+
}
68+
if (ref == null) {
69+
// map the initiator
70+
var type
71+
switch (initiator) {
72+
case 1: // PROXY_INITIATOR
73+
type = 1 // Proxied
74+
break
75+
case 2: // ACTIVE_SCANNER_INITIATOR
76+
type = 3 // Scanner
77+
break
78+
case 3: // SPIDER_INITIATOR
79+
type = 2 // Spider
80+
break
81+
case 4: // FUZZER_INITIATOR
82+
type = 8 // Fuzzer
83+
break
84+
case 5: // AUTHENTICATION_INITIATOR
85+
type = 15 // User
86+
break
87+
case 6: // MANUAL_REQUEST_INITIATOR
88+
type = 15 // User
89+
break
90+
case 8: // BEAN_SHELL_INITIATOR
91+
type = 15 // User
92+
break
93+
case 9: // ACCESS_CONTROL_SCANNER_INITIATOR
94+
type = 13 // Access control
95+
break
96+
default:
97+
type = 15 // User - fallback
98+
break
99+
}
100+
ref = new org.parosproxy.paros.model.HistoryReference(model.getSession(), type, msg)
101+
}
102+
alert.setMessage(msg)
103+
alert.setUri(msg.getRequestHeader().getURI().toString())
104+
alert.setDescription("A response code of " + code + " was returned by the server.\n" +
105+
"This may indicate that the application is failing to handle unexpected input correctly.\n" +
106+
"Raised by the 'Alert on HTTP Response Code Error' script");
107+
// Use a regex to extract the evidence from the response header
108+
var regex = new RegExp("^HTTP.*" + code)
109+
alert.setEvidence(msg.getResponseHeader().toString().match(regex))
110+
alert.setCweId(388) // CWE CATEGORY: Error Handling
111+
alert.setWascId(20) // WASC Improper Input Handling
112+
extensionAlert.alertFound(alert , ref)
113+
}
114+
}
115+
}
116+
117+
function isGloballyExcluded(msg) {
118+
var url = msg.getRequestHeader().getURI().toString()
119+
var regexes = model.getSession().getGlobalExcludeURLRegexs()
120+
for (var i in regexes) {
121+
if (Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE).matcher(url).matches()) {
122+
return true
123+
}
124+
}
125+
return false
126+
}
127+
128+
- type: script
129+
parameters:
130+
action: add
131+
type: httpsender
132+
engine: "ECMAScript : Graal.js"
133+
name: AlertOnUnexpectedContentTypes.js
134+
inline: |
135+
var Pattern = Java.type("java.util.regex.Pattern")
136+
137+
var pluginid = 100001 // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md
138+
139+
var extensionAlert = control.getExtensionLoader().getExtension(org.zaproxy.zap.extension.alert.ExtensionAlert.NAME)
140+
141+
var expectedTypes = [
142+
"application/octet-stream",
143+
"text/plain"
144+
]
145+
146+
var expectedTypeGroups = ["json", "yaml", "xml"]
147+
148+
function sendingRequest(msg, initiator, helper) {
149+
// Nothing to do
150+
}
151+
152+
function responseReceived(msg, initiator, helper) {
153+
if (isGloballyExcluded(msg)) {
154+
// Not of interest.
155+
return
156+
}
157+
158+
if (extensionAlert != null) {
159+
var ctype = msg.getResponseHeader().getHeader("Content-Type")
160+
if (ctype != null) {
161+
if (ctype.indexOf(";") > 0) {
162+
ctype = ctype.substring(0, ctype.indexOf(";"))
163+
}
164+
if (!msg.getResponseHeader().hasContentType(expectedTypeGroups) && expectedTypes.indexOf(ctype) < 0) {
165+
// Another rule will complain if theres no type
166+
167+
var risk = 1 // Low
168+
var title = "Unexpected Content-Type was returned"
169+
// CONFIDENCE_HIGH = 3 (we can be pretty sure we're right)
170+
var alert = new org.parosproxy.paros.core.scanner.Alert(pluginid, risk, 3, title)
171+
var ref = msg.getHistoryRef()
172+
if (ref != null && org.parosproxy.paros.model.HistoryReference.getTemporaryTypes().contains(
173+
java.lang.Integer.valueOf(ref.getHistoryType()))) {
174+
// Dont use temporary types as they will get deleted
175+
ref = null
176+
}
177+
if (ref == null) {
178+
// map the initiator
179+
var type
180+
switch (initiator) {
181+
case 1: // PROXY_INITIATOR
182+
type = 1 // Proxied
183+
break
184+
case 2: // ACTIVE_SCANNER_INITIATOR
185+
type = 3 // Scanner
186+
break
187+
case 3: // SPIDER_INITIATOR
188+
type = 2 // Spider
189+
break
190+
case 4: // FUZZER_INITIATOR
191+
type = 8 // Fuzzer
192+
break
193+
case 5: // AUTHENTICATION_INITIATOR
194+
type = 15 // User
195+
break
196+
case 6: // MANUAL_REQUEST_INITIATOR
197+
type = 15 // User
198+
break
199+
case 8: // BEAN_SHELL_INITIATOR
200+
type = 15 // User
201+
break
202+
case 9: // ACCESS_CONTROL_SCANNER_INITIATOR
203+
type = 13 // Access control
204+
break
205+
default:
206+
type = 15 // User - fallback
207+
break
208+
}
209+
ref = new org.parosproxy.paros.model.HistoryReference(model.getSession(), type, msg)
210+
}
211+
alert.setMessage(msg)
212+
alert.setUri(msg.getRequestHeader().getURI().toString())
213+
alert.setDescription("A Content-Type of " + ctype + " was returned by the server.\n" +
214+
"This is not one of the types expected to be returned by an API.\n" +
215+
"Raised by the 'Alert on Unexpected Content Types' script");
216+
alert.setEvidence(ctype)
217+
extensionAlert.alertFound(alert , ref)
218+
}
219+
}
220+
}
221+
}
222+
223+
function isGloballyExcluded(msg) {
224+
var url = msg.getRequestHeader().getURI().toString()
225+
var regexes = model.getSession().getGlobalExcludeURLRegexs()
226+
for (var i in regexes) {
227+
if (Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE).matcher(url).matches()) {
228+
return true
229+
}
230+
}
231+
return false
232+
}
233+
234+
- type: "graphql"
235+
parameters:
236+
endpoint: # String: the endpoint URL, default: null, no schema is imported
237+
schemaUrl: # String: URL pointing to a GraphQL Schema, default: null, import using introspection on endpoint
238+
schemaFile: # String: Local file path of a GraphQL Schema, default: null, import using schemaUrl
239+
240+
- type: "openapi"
241+
parameters:
242+
apiFile: # String: Local file containing the OpenAPI definition, default: null, no definition will be imported
243+
apiUrl: # String: URL containing the OpenAPI definition, default: null, no definition will be imported
244+
targetUrl: # String: URL which overrides the target defined in the definition, default: null, the target will not be overridden
245+
246+
- type: "soap"
247+
parameters:
248+
wsdlFile: # String: Local file path of the WSDL, default: null, no definition will be imported
249+
wsdlUrl: # String: URL pointing to the WSDL, default: null, no definition will be imported
250+
251+
- parameters:
252+
policyDefinition:
253+
defaultStrength: "medium"
254+
defaultThreshold: "Off"
255+
rules:
256+
- id: 0
257+
name: "Directory Browsing"
258+
threshold: "medium"
259+
- id: 7
260+
name: "Remote File Inclusion"
261+
threshold: "medium"
262+
- id: 20019
263+
name: "External Redirect"
264+
threshold: "medium"
265+
- id: 30001
266+
name: "Buffer Overflow"
267+
threshold: "medium"
268+
- id: 30002
269+
name: "Format String Error"
270+
threshold: "medium"
271+
- id: 30003
272+
name: "Integer Overflow Error"
273+
threshold: "medium"
274+
- id: 40003
275+
name: "CRLF Injection"
276+
threshold: "medium"
277+
- id: 40008
278+
name: "Parameter Tampering"
279+
threshold: "medium"
280+
- id: 40009
281+
name: "Server Side Include"
282+
threshold: "medium"
283+
- id: 40018
284+
name: " SQL Injection"
285+
threshold: "medium"
286+
- id: 40042
287+
name: "Spring Actuator Information Leak"
288+
threshold: "medium"
289+
- id: 40044
290+
name: "Exponential Entity Expansion (Billion Laughs Attack)"
291+
threshold: "medium"
292+
- id: 90017
293+
name: "XSLT Injection"
294+
threshold: "medium"
295+
- id: 90019
296+
name: "Server Side Code Injection"
297+
threshold: "medium"
298+
- id: 90020
299+
name: "Remote OS Command Injection"
300+
threshold: "medium"
301+
- id: 90021
302+
name: "XPath Injection"
303+
threshold: "medium"
304+
- id: 90023
305+
name: "XML External Entity Attack"
306+
threshold: "medium"
307+
- id: 90025
308+
name: "Expression Language Injection"
309+
threshold: "medium"
310+
- id: 90026
311+
name: "SOAP Action Spoofing"
312+
threshold: "medium"
313+
- id: 90029
314+
name: "SOAP XML Injection"
315+
threshold: "medium"
316+
- id: 90034
317+
name: "Cloud Metadata Potentially Exposed"
318+
- id: 90035
319+
name: "Server Side Template Injection"
320+
threshold: "medium"
321+
- id: 90036
322+
name: "Server Side Template Injection (Blind)"
323+
threshold: "medium"
324+
325+
name: "activeScan"
326+
type: "activeScan"
327+
- parameters: {}
328+
name: "passiveScan-wait-pre-report"
329+
type: "passiveScan-wait"
330+
- parameters:
331+
template: "modern"
332+
reportTitle: "ZAP Scanning Report"
333+
reportDescription: ""
334+
name: "report"
335+
type: "report"

0 commit comments

Comments
 (0)