Skip to content

Commit 1cb590a

Browse files
committed
pscanrules: Charset Mismatch add example alerts
- CHANGELOG > Add note. - CharsetMismatchScanRule > Add example alerts, adjust handling, some minor related clean code changes. Drop alert related to "older clients". - CharsetMismatchScanRuleUnitTest > Add test to assert the example details, use parameterized case where practical. - Messages.properties > Clarify one of the descriptions. - Help > Drop details related to "older clients" alert. Signed-off-by: kingthorin <[email protected]>
1 parent 34ef7cb commit 1cb590a

File tree

5 files changed

+119
-248
lines changed

5 files changed

+119
-248
lines changed

addOns/pscanrules/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
## Unreleased
77
### Changed
88
- Reduced usage of error level logging.
9+
- The Charset Mismatch scan rule now includes example alert functionality for documentation generation purposes (Issue 6119) and alert references (Issue 7100).
10+
11+
### Removed
12+
- The Charset Mismatch scan rule no longer produces an alert with regard to META content-type and older clients.
913

1014
## [68] - 2025-10-21
1115
### Added

addOns/pscanrules/src/main/java/org/zaproxy/zap/extension/pscanrules/CharsetMismatchScanRule.java

Lines changed: 75 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*/
2020
package org.zaproxy.zap.extension.pscanrules;
2121

22+
import java.nio.charset.StandardCharsets;
23+
import java.util.Arrays;
2224
import java.util.Collections;
2325
import java.util.HashMap;
2426
import java.util.List;
@@ -48,6 +50,8 @@ public class CharsetMismatchScanRule extends PluginPassiveScanner
4850
/** Prefix for internationalized messages used by this rule */
4951
private static final String MESSAGE_PREFIX = "pscanrules.charsetmismatch.";
5052

53+
private static final int PLUGIN_ID = 90011;
54+
5155
private static final Map<String, String> ALERT_TAGS;
5256

5357
static {
@@ -58,39 +62,50 @@ public class CharsetMismatchScanRule extends PluginPassiveScanner
5862
ALERT_TAGS = Collections.unmodifiableMap(alertTags);
5963
}
6064

61-
private static enum MismatchType {
62-
NO_MISMATCH_METACONTENTTYPE_MISSING,
63-
HEADER_METACONTENTYPE_MISMATCH,
64-
HEADER_METACHARSET_MISMATCH,
65-
METACONTENTTYPE_METACHARSET_MISMATCH,
66-
XML_MISMATCH
65+
private enum MismatchType {
66+
HEADER_METACONTENTYPE_MISMATCH(
67+
"-1",
68+
"name.header_metacontentype_mismatch",
69+
"extrainfo.html.header_metacontentype_mismatch"),
70+
HEADER_METACHARSET_MISMATCH(
71+
"-2",
72+
"name.header_metacharset_mismatch",
73+
"extrainfo.html.header_metacharset_mismatch"),
74+
METACONTENTTYPE_METACHARSET_MISMATCH(
75+
"-3",
76+
"name.metacontenttype_metacharset_mismatch",
77+
"extrainfo.html.metacontenttype_metacharset_mismatch"),
78+
XML_MISMATCH("-4", "name", "extrainfo.xml");
79+
80+
private final String alertRef;
81+
private final String name;
82+
private final String otherInfoKey;
83+
84+
MismatchType(String ref, String nameKey, String otherInfoKey) {
85+
this.alertRef = PLUGIN_ID + ref;
86+
this.name = Constant.messages.getString(MESSAGE_PREFIX + nameKey);
87+
this.otherInfoKey = otherInfoKey;
88+
}
89+
90+
String getAlertRef() {
91+
return this.alertRef;
92+
}
93+
94+
String getName() {
95+
return this.name;
96+
}
97+
98+
private String getExtraInfo(String firstCharset, String secondCharset) {
99+
return Constant.messages.getString(
100+
MESSAGE_PREFIX + otherInfoKey, firstCharset, secondCharset);
101+
}
67102
}
68103

69104
@Override
70105
public String getName() {
71106
return Constant.messages.getString(MESSAGE_PREFIX + "name");
72107
}
73108

74-
public String getVariant(MismatchType currentType) {
75-
switch (currentType) {
76-
case NO_MISMATCH_METACONTENTTYPE_MISSING: // no_mismatch_metacontenttype_missing
77-
return Constant.messages.getString(
78-
MESSAGE_PREFIX + "variant.no_mismatch_metacontenttype_missing");
79-
case HEADER_METACONTENTYPE_MISMATCH: // header_metacontentype_mismatch
80-
return Constant.messages.getString(
81-
MESSAGE_PREFIX + "variant.header_metacontentype_mismatch");
82-
case HEADER_METACHARSET_MISMATCH: // header_metacharset_mismatch
83-
return Constant.messages.getString(
84-
MESSAGE_PREFIX + "variant.header_metacharset_mismatch");
85-
case METACONTENTTYPE_METACHARSET_MISMATCH: // metacontenttype_metacharset_mismatch
86-
return Constant.messages.getString(
87-
MESSAGE_PREFIX + "variant.metacontenttype_metacharset_mismatch");
88-
case XML_MISMATCH:
89-
default:
90-
return "";
91-
}
92-
}
93-
94109
@Override
95110
public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
96111
if (msg.getResponseBody().length() == 0) {
@@ -155,55 +170,31 @@ public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
155170
// other
156171
if (AlertThreshold.LOW.equals(pluginThreshold)
157172
&& !bodyContentCharset.equalsIgnoreCase(metaCharset)) {
158-
raiseAlert(
159-
msg,
160-
id,
161-
metaCharset,
162-
bodyContentCharset,
163-
MismatchType
164-
.METACONTENTTYPE_METACHARSET_MISMATCH); // body declarations
165-
// inconsistent with
166-
// each other
173+
buildAlert(
174+
metaCharset,
175+
bodyContentCharset,
176+
MismatchType.METACONTENTTYPE_METACHARSET_MISMATCH)
177+
.raise();
167178
}
168179
}
169180
if (hasBodyCharset) {
170181
// Check the body content type charset declaration against the header
171182
if (!bodyContentCharset.equalsIgnoreCase(headerCharset)) {
172-
raiseAlert(
173-
msg,
174-
id,
175-
headerCharset,
176-
bodyContentCharset,
177-
MismatchType.HEADER_METACONTENTYPE_MISMATCH); // body declaration
178-
// doesn't match header
183+
buildAlert(
184+
headerCharset,
185+
bodyContentCharset,
186+
MismatchType.HEADER_METACONTENTYPE_MISMATCH)
187+
.raise();
179188
}
180189
}
181190
if (hasMetaCharset) {
182191
// Check the body meta charset declaration against the header
183192
if (!metaCharset.equalsIgnoreCase(headerCharset)) {
184-
raiseAlert(
185-
msg,
186-
id,
187-
headerCharset,
188-
metaCharset,
189-
MismatchType
190-
.HEADER_METACHARSET_MISMATCH); // body declaration doesn't
191-
// match header
192-
}
193-
// If Threshold is LOW be picky and report that
194-
// only a meta charset declaration might be insufficient coverage for older
195-
// clients
196-
if (AlertThreshold.LOW.equals(pluginThreshold) && hasBodyCharset == false) {
197-
raiseAlert(
198-
msg,
199-
id,
200-
"",
201-
"",
202-
MismatchType
203-
.NO_MISMATCH_METACONTENTTYPE_MISSING); // body declaration
204-
// does match header
205-
// but may overlook
206-
// older clients
193+
buildAlert(
194+
headerCharset,
195+
metaCharset,
196+
MismatchType.HEADER_METACHARSET_MISMATCH)
197+
.raise();
207198
}
208199
}
209200
}
@@ -214,11 +205,11 @@ public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
214205
// TODO: could there be more than one XML declaration tag for a single XML file?
215206
List<StartTag> xmlDeclarationTags =
216207
source.getAllStartTags(StartTagType.XML_DECLARATION);
217-
if (xmlDeclarationTags.size() > 0) {
208+
if (!xmlDeclarationTags.isEmpty()) {
218209
StartTag xmlDeclarationTag = xmlDeclarationTags.get(0);
219210
String encoding = xmlDeclarationTag.getAttributeValue("encoding");
220211
if (!headerCharset.equalsIgnoreCase(encoding)) {
221-
raiseAlert(msg, id, headerCharset, encoding, MismatchType.XML_MISMATCH);
212+
buildAlert(headerCharset, encoding, MismatchType.XML_MISMATCH).raise();
222213
}
223214
}
224215
}
@@ -261,33 +252,24 @@ private static String getBodyContentCharset(String bodyContentType) {
261252
return charset;
262253
}
263254

264-
private void raiseAlert(
265-
HttpMessage msg,
266-
int id,
267-
String firstCharset,
268-
String secondCharset,
269-
MismatchType currentMismatch) {
270-
newAlert()
271-
.setName(
272-
getName()
273-
+ " "
274-
+ getVariant(
275-
currentMismatch)) // Compound name (to account for variant
276-
// designations, and multiple alerts on single URI)
255+
private AlertBuilder buildAlert(
256+
String firstCharset, String secondCharset, MismatchType currentMismatch) {
257+
return newAlert()
258+
.setName(currentMismatch.getName())
277259
.setRisk(getRisk())
278260
.setConfidence(Alert.CONFIDENCE_LOW)
279261
.setDescription(getDescription())
280-
.setOtherInfo(getExtraInfo(firstCharset, secondCharset, currentMismatch))
262+
.setOtherInfo(currentMismatch.getExtraInfo(firstCharset, secondCharset))
281263
.setSolution(getSolution())
282264
.setReference(getReference())
283265
.setCweId(getCweId())
284266
.setWascId(getWascId())
285-
.raise();
267+
.setAlertRef(currentMismatch.getAlertRef());
286268
}
287269

288270
@Override
289271
public int getPluginId() {
290-
return 90011;
272+
return PLUGIN_ID;
291273
}
292274

293275
/*
@@ -323,46 +305,15 @@ public Map<String, String> getAlertTags() {
323305
return ALERT_TAGS;
324306
}
325307

326-
private static String getExtraInfo(
327-
String firstCharset, String secondCharset, MismatchType mismatchType) {
328-
329-
String extraInfo = "";
330-
331-
switch (mismatchType) {
332-
case NO_MISMATCH_METACONTENTTYPE_MISSING: // no_mismatch_metacontenttype_missing
333-
extraInfo =
334-
Constant.messages.getString(
335-
MESSAGE_PREFIX
336-
+ "extrainfo.html.no_mismatch_metacontenttype_missing");
337-
break;
338-
case HEADER_METACONTENTYPE_MISMATCH: // header_metacontentype_mismatch
339-
extraInfo =
340-
Constant.messages.getString(
341-
MESSAGE_PREFIX + "extrainfo.html.header_metacontentype_mismatch",
342-
firstCharset,
343-
secondCharset);
344-
break;
345-
case HEADER_METACHARSET_MISMATCH: // header_metacharset_mismatch
346-
extraInfo =
347-
Constant.messages.getString(
348-
MESSAGE_PREFIX + "extrainfo.html.header_metacharset_mismatch",
349-
firstCharset,
350-
secondCharset);
351-
break;
352-
case METACONTENTTYPE_METACHARSET_MISMATCH: // metacontenttype_metacharset_mismatch
353-
extraInfo =
354-
Constant.messages.getString(
355-
MESSAGE_PREFIX
356-
+ "extrainfo.html.metacontenttype_metacharset_mismatch",
357-
firstCharset,
358-
secondCharset);
359-
break;
360-
case XML_MISMATCH:
361-
extraInfo =
362-
Constant.messages.getString(
363-
MESSAGE_PREFIX + "extrainfo.xml", firstCharset, secondCharset);
364-
break;
365-
}
366-
return extraInfo;
308+
@Override
309+
public List<Alert> getExampleAlerts() {
310+
return Arrays.stream(MismatchType.values())
311+
.map(
312+
mismatchType -> {
313+
return buildAlert(
314+
StandardCharsets.UTF_8.name(), "ISO-123", mismatchType)
315+
.build();
316+
})
317+
.toList();
367318
}
368319
}

addOns/pscanrules/src/main/javahelp/org/zaproxy/zap/extension/pscanrules/resources/help/contents/pscanrules.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ <H2 id="id-90011">Charset Mismatch</H2>
8989
</li>
9090
<li>Low Threshold:
9191
<ul>
92-
<li>Meta Content-Type Charset Missing - The response doesn't contain a META Content-Type declaration, which may overlook older clients.</li>
9392
<li>Meta Charset Versus Meta Content-Type Charset - The response contains both a META Content-Type declaration and a META Charset declaration, and they don't match.</li>
9493
</ul>
9594
</li>

addOns/pscanrules/src/main/resources/org/zaproxy/zap/extension/pscanrules/resources/Messages.properties

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,13 @@ pscanrules.charsetmismatch.desc = This check identifies responses where the HTTP
4545
pscanrules.charsetmismatch.extrainfo.html.header_metacharset_mismatch = There was a charset mismatch between the HTTP Header and the META charset encoding declaration: [{0}] and [{1}] do not match.
4646
pscanrules.charsetmismatch.extrainfo.html.header_metacontentype_mismatch = There was a charset mismatch between the HTTP Header and the META content-type encoding declarations: [{0}] and [{1}] do not match.
4747
pscanrules.charsetmismatch.extrainfo.html.metacontenttype_metacharset_mismatch = There was a charset mismatch between the META charset and the META content-type encoding declaration: [{0}] and [{1}] do not match.
48-
pscanrules.charsetmismatch.extrainfo.html.no_mismatch_metacontenttype_missing = Charset is defined only by META charset, older clients that expect character set to be defined by META content-type may not correctly display this content.
4948
pscanrules.charsetmismatch.extrainfo.xml = There was a charset mismatch between the HTTP Header and the XML encoding declaration: [{0}] and [{1}] do not match.
5049
pscanrules.charsetmismatch.name = Charset Mismatch
50+
pscanrules.charsetmismatch.name.header_metacharset_mismatch = Charset Mismatch (Header Versus Meta Charset)
51+
pscanrules.charsetmismatch.name.header_metacontentype_mismatch = Charset Mismatch (Header Versus Meta Content-Type Charset)
52+
pscanrules.charsetmismatch.name.metacontenttype_metacharset_mismatch = Charset Mismatch (Meta Charset Versus Meta Content-Type Charset)
5153
pscanrules.charsetmismatch.refs = https://code.google.com/p/browsersec/wiki/Part2#Character_set_handling_and_detection
5254
pscanrules.charsetmismatch.soln = Force UTF-8 for all text content in both the HTTP header and meta tags in HTML or encoding declarations in XML.
53-
pscanrules.charsetmismatch.variant.header_metacharset_mismatch = (Header Versus Meta Charset)
54-
pscanrules.charsetmismatch.variant.header_metacontentype_mismatch = (Header Versus Meta Content-Type Charset)
55-
pscanrules.charsetmismatch.variant.metacontenttype_metacharset_mismatch = (Meta Charset Versus Meta Content-Type Charset)
56-
pscanrules.charsetmismatch.variant.no_mismatch_metacontenttype_missing = (Meta Content-Type Charset Missing)
5755
5856
pscanrules.contentsecuritypolicymissing.desc = Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.
5957
pscanrules.contentsecuritypolicymissing.name = Content Security Policy (CSP) Header Not Set

0 commit comments

Comments
 (0)