forked from zaproxy/community-scripts
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathAlertOnUnexpectedContentTypes.js
More file actions
125 lines (114 loc) · 3.83 KB
/
Copy pathAlertOnUnexpectedContentTypes.js
File metadata and controls
125 lines (114 loc) · 3.83 KB
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
// A script which will raise alerts based on unexpected Content-Types
// By default it will raise 'Low' level alerts for content types that are not expected to be returned by APIs.
// But it can be easily changed.
const Integer = Java.type("java.lang.Integer");
const Pattern = Java.type("java.util.regex.Pattern");
const Alert = Java.type("org.parosproxy.paros.core.scanner.Alert");
const ExtensionAlert = Java.type(
"org.zaproxy.zap.extension.alert.ExtensionAlert"
);
const HistoryReference = Java.type(
"org.parosproxy.paros.model.HistoryReference"
);
const extensionAlert = control
.getExtensionLoader()
.getExtension(ExtensionAlert.NAME);
var pluginid = 100001; // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md
var expectedTypes = ["application/octet-stream", "text/plain"];
var expectedTypeGroups = ["json", "yaml", "xml"];
function sendingRequest(msg, initiator, helper) {
// Nothing to do
}
function responseReceived(msg, initiator, helper) {
if (isGloballyExcluded(msg)) {
// Not of interest.
return;
}
if (extensionAlert != null) {
var ctype = msg.getResponseHeader().getHeader("Content-Type");
if (ctype != null) {
if (ctype.indexOf(";") > 0) {
ctype = ctype.substring(0, ctype.indexOf(";"));
}
if (
!msg.getResponseHeader().hasContentType(expectedTypeGroups) &&
expectedTypes.indexOf(ctype) < 0
) {
// Another rule will complain if theres no type
var risk = 1; // Low
var title = "Unexpected Content-Type was returned";
// CONFIDENCE_HIGH = 3 (we can be pretty sure we're right)
var alert = new Alert(pluginid, risk, 3, title);
var ref = msg.getHistoryRef();
if (
ref != null &&
HistoryReference.getTemporaryTypes().contains(
Integer.valueOf(ref.getHistoryType())
)
) {
// Dont use temporary types as they will get deleted
ref = null;
}
if (ref == null) {
// map the initiator
var type;
switch (initiator) {
case 1: // PROXY_INITIATOR
type = 1; // Proxied
break;
case 2: // ACTIVE_SCANNER_INITIATOR
type = 3; // Scanner
break;
case 3: // SPIDER_INITIATOR
type = 2; // Spider
break;
case 4: // FUZZER_INITIATOR
type = 8; // Fuzzer
break;
case 5: // AUTHENTICATION_INITIATOR
type = 15; // User
break;
case 6: // MANUAL_REQUEST_INITIATOR
type = 15; // User
break;
case 8: // BEAN_SHELL_INITIATOR
type = 15; // User
break;
case 9: // ACCESS_CONTROL_SCANNER_INITIATOR
type = 13; // Access control
break;
default:
type = 15; // User - fallback
break;
}
ref = new HistoryReference(model.getSession(), type, msg);
}
alert.setMessage(msg);
alert.setUri(msg.getRequestHeader().getURI().toString());
alert.setDescription(
"A Content-Type of " +
ctype +
" was returned by the server.\n" +
"This is not one of the types expected to be returned by an API.\n" +
"Raised by the 'Alert on Unexpected Content Types' script"
);
alert.setEvidence(ctype);
extensionAlert.alertFound(alert, ref);
}
}
}
}
function isGloballyExcluded(msg) {
var url = msg.getRequestHeader().getURI().toString();
var regexes = model.getSession().getGlobalExcludeURLRegexs();
for (var i in regexes) {
if (
Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE)
.matcher(url)
.matches()
) {
return true;
}
}
return false;
}