Skip to content

Commit 06bff07

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 30618bc commit 06bff07

File tree

5 files changed

+123
-206
lines changed

5 files changed

+123
-206
lines changed

addOns/pscanrules/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
77
### Changed
88
- Add alert references to HTTP Server Response Header scan rule alerts (Issue 7100, 9050).
99
- Update alert references to latest locations to fix 404s and resolve redirections.
10+
- The Charset Mismatch scan rule now includes example alert functionality for documentation generation purposes (Issue 6119) and alert references (Issue 7100).
11+
12+
### Removed
13+
- The Charset Mismatch scan rule no longer produces an alert with regard to META content-type and older clients.
1014

1115
## [66] - 2025-07-25
1216
### Added

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

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

22+
import java.nio.charset.StandardCharsets;
2223
import java.util.Collections;
2324
import java.util.HashMap;
2425
import java.util.List;
@@ -47,6 +48,8 @@ public class CharsetMismatchScanRule extends PluginPassiveScanner
4748
/** Prefix for internationalized messages used by this rule */
4849
private static final String MESSAGE_PREFIX = "pscanrules.charsetmismatch.";
4950

51+
private static final int PLUGIN_ID = 90011;
52+
5053
private static final Map<String, String> ALERT_TAGS;
5154

5255
static {
@@ -56,39 +59,43 @@ public class CharsetMismatchScanRule extends PluginPassiveScanner
5659
ALERT_TAGS = Collections.unmodifiableMap(alertTags);
5760
}
5861

59-
private static enum MismatchType {
60-
NO_MISMATCH_METACONTENTTYPE_MISSING,
61-
HEADER_METACONTENTYPE_MISMATCH,
62-
HEADER_METACHARSET_MISMATCH,
63-
METACONTENTTYPE_METACHARSET_MISMATCH,
64-
XML_MISMATCH
62+
private enum MismatchType {
63+
HEADER_METACONTENTYPE_MISMATCH(
64+
"-1",
65+
Constant.messages.getString(
66+
MESSAGE_PREFIX + "variant.header_metacontentype_mismatch")),
67+
HEADER_METACHARSET_MISMATCH(
68+
"-2",
69+
Constant.messages.getString(
70+
MESSAGE_PREFIX + "variant.header_metacharset_mismatch")),
71+
METACONTENTTYPE_METACHARSET_MISMATCH(
72+
"-3",
73+
Constant.messages.getString(
74+
MESSAGE_PREFIX + "variant.metacontenttype_metacharset_mismatch")),
75+
XML_MISMATCH("-4", "");
76+
77+
private final String alertRef;
78+
private final String variant;
79+
80+
MismatchType(String ref, String variant) {
81+
this.alertRef = String.valueOf(PLUGIN_ID) + ref;
82+
this.variant = variant;
83+
}
84+
85+
String getAlertRef() {
86+
return this.alertRef;
87+
}
88+
89+
String getVariant() {
90+
return this.variant;
91+
}
6592
}
6693

6794
@Override
6895
public String getName() {
6996
return Constant.messages.getString(MESSAGE_PREFIX + "name");
7097
}
7198

72-
public String getVariant(MismatchType currentType) {
73-
switch (currentType) {
74-
case NO_MISMATCH_METACONTENTTYPE_MISSING: // no_mismatch_metacontenttype_missing
75-
return Constant.messages.getString(
76-
MESSAGE_PREFIX + "variant.no_mismatch_metacontenttype_missing");
77-
case HEADER_METACONTENTYPE_MISMATCH: // header_metacontentype_mismatch
78-
return Constant.messages.getString(
79-
MESSAGE_PREFIX + "variant.header_metacontentype_mismatch");
80-
case HEADER_METACHARSET_MISMATCH: // header_metacharset_mismatch
81-
return Constant.messages.getString(
82-
MESSAGE_PREFIX + "variant.header_metacharset_mismatch");
83-
case METACONTENTTYPE_METACHARSET_MISMATCH: // metacontenttype_metacharset_mismatch
84-
return Constant.messages.getString(
85-
MESSAGE_PREFIX + "variant.metacontenttype_metacharset_mismatch");
86-
case XML_MISMATCH:
87-
default:
88-
return "";
89-
}
90-
}
91-
9299
@Override
93100
public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
94101
if (msg.getResponseBody().length() == 0) {
@@ -153,55 +160,31 @@ public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
153160
// other
154161
if (AlertThreshold.LOW.equals(pluginThreshold)
155162
&& !bodyContentCharset.equalsIgnoreCase(metaCharset)) {
156-
raiseAlert(
157-
msg,
158-
id,
159-
metaCharset,
160-
bodyContentCharset,
161-
MismatchType
162-
.METACONTENTTYPE_METACHARSET_MISMATCH); // body declarations
163-
// inconsistent with
164-
// each other
163+
buildAlert(
164+
metaCharset,
165+
bodyContentCharset,
166+
MismatchType.METACONTENTTYPE_METACHARSET_MISMATCH)
167+
.raise(); // body declarations inconsistent with each other
165168
}
166169
}
167170
if (hasBodyCharset) {
168171
// Check the body content type charset declaration against the header
169172
if (!bodyContentCharset.equalsIgnoreCase(headerCharset)) {
170-
raiseAlert(
171-
msg,
172-
id,
173-
headerCharset,
174-
bodyContentCharset,
175-
MismatchType.HEADER_METACONTENTYPE_MISMATCH); // body declaration
176-
// doesn't match header
173+
buildAlert(
174+
headerCharset,
175+
bodyContentCharset,
176+
MismatchType.HEADER_METACONTENTYPE_MISMATCH)
177+
.raise(); // body declaration doesn't match header
177178
}
178179
}
179180
if (hasMetaCharset) {
180181
// Check the body meta charset declaration against the header
181182
if (!metaCharset.equalsIgnoreCase(headerCharset)) {
182-
raiseAlert(
183-
msg,
184-
id,
185-
headerCharset,
186-
metaCharset,
187-
MismatchType
188-
.HEADER_METACHARSET_MISMATCH); // body declaration doesn't
189-
// match header
190-
}
191-
// If Threshold is LOW be picky and report that
192-
// only a meta charset declaration might be insufficient coverage for older
193-
// clients
194-
if (AlertThreshold.LOW.equals(pluginThreshold) && hasBodyCharset == false) {
195-
raiseAlert(
196-
msg,
197-
id,
198-
"",
199-
"",
200-
MismatchType
201-
.NO_MISMATCH_METACONTENTTYPE_MISSING); // body declaration
202-
// does match header
203-
// but may overlook
204-
// older clients
183+
buildAlert(
184+
headerCharset,
185+
metaCharset,
186+
MismatchType.HEADER_METACHARSET_MISMATCH)
187+
.raise(); // body declaration doesn't match header
205188
}
206189
}
207190
}
@@ -212,11 +195,11 @@ public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
212195
// TODO: could there be more than one XML declaration tag for a single XML file?
213196
List<StartTag> xmlDeclarationTags =
214197
source.getAllStartTags(StartTagType.XML_DECLARATION);
215-
if (xmlDeclarationTags.size() > 0) {
198+
if (!xmlDeclarationTags.isEmpty()) {
216199
StartTag xmlDeclarationTag = xmlDeclarationTags.get(0);
217200
String encoding = xmlDeclarationTag.getAttributeValue("encoding");
218201
if (!headerCharset.equalsIgnoreCase(encoding)) {
219-
raiseAlert(msg, id, headerCharset, encoding, MismatchType.XML_MISMATCH);
202+
buildAlert(headerCharset, encoding, MismatchType.XML_MISMATCH).raise();
220203
}
221204
}
222205
}
@@ -259,19 +242,11 @@ private static String getBodyContentCharset(String bodyContentType) {
259242
return charset;
260243
}
261244

262-
private void raiseAlert(
263-
HttpMessage msg,
264-
int id,
265-
String firstCharset,
266-
String secondCharset,
267-
MismatchType currentMismatch) {
268-
newAlert()
269-
.setName(
270-
getName()
271-
+ " "
272-
+ getVariant(
273-
currentMismatch)) // Compound name (to account for variant
274-
// designations, and multiple alerts on single URI)
245+
private AlertBuilder buildAlert(
246+
String firstCharset, String secondCharset, MismatchType currentMismatch) {
247+
// Compound name (to account for variant designations, and multiple alerts on single URI)
248+
return newAlert()
249+
.setName(getName() + " " + currentMismatch.getVariant())
275250
.setRisk(getRisk())
276251
.setConfidence(Alert.CONFIDENCE_LOW)
277252
.setDescription(getDescription())
@@ -280,12 +255,12 @@ private void raiseAlert(
280255
.setReference(getReference())
281256
.setCweId(getCweId())
282257
.setWascId(getWascId())
283-
.raise();
258+
.setAlertRef(currentMismatch.getAlertRef());
284259
}
285260

286261
@Override
287262
public int getPluginId() {
288-
return 90011;
263+
return PLUGIN_ID;
289264
}
290265

291266
/*
@@ -327,12 +302,6 @@ private static String getExtraInfo(
327302
String extraInfo = "";
328303

329304
switch (mismatchType) {
330-
case NO_MISMATCH_METACONTENTTYPE_MISSING: // no_mismatch_metacontenttype_missing
331-
extraInfo =
332-
Constant.messages.getString(
333-
MESSAGE_PREFIX
334-
+ "extrainfo.html.no_mismatch_metacontenttype_missing");
335-
break;
336305
case HEADER_METACONTENTYPE_MISMATCH: // header_metacontentype_mismatch
337306
extraInfo =
338307
Constant.messages.getString(
@@ -363,4 +332,26 @@ private static String getExtraInfo(
363332
}
364333
return extraInfo;
365334
}
335+
336+
@Override
337+
public List<Alert> getExampleAlerts() {
338+
return List.of(
339+
buildAlert(
340+
StandardCharsets.UTF_8.name(),
341+
"ISO-123",
342+
MismatchType.HEADER_METACONTENTYPE_MISMATCH)
343+
.build(),
344+
buildAlert(
345+
StandardCharsets.UTF_8.name(),
346+
"ISO-123",
347+
MismatchType.HEADER_METACHARSET_MISMATCH)
348+
.build(),
349+
buildAlert(
350+
"ISO-123",
351+
StandardCharsets.UTF_8.name(),
352+
MismatchType.METACONTENTTYPE_METACHARSET_MISMATCH)
353+
.build(),
354+
buildAlert(StandardCharsets.UTF_8.name(), "ISO-123", MismatchType.XML_MISMATCH)
355+
.build());
356+
}
366357
}

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: 0 additions & 2 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
5150
pscanrules.charsetmismatch.refs = https://code.google.com/p/browsersec/wiki/Part2#Character_set_handling_and_detection
5251
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.
5352
pscanrules.charsetmismatch.variant.header_metacharset_mismatch = (Header Versus Meta Charset)
5453
pscanrules.charsetmismatch.variant.header_metacontentype_mismatch = (Header Versus Meta Content-Type Charset)
5554
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)