Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions addOns/pscanrules/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Changed
- Reduced usage of error level logging.
- The Charset Mismatch scan rule now includes example alert functionality for documentation generation purposes (Issue 6119) and alert references (Issue 7100).

### Removed
- The Charset Mismatch scan rule no longer produces an alert with regard to META content-type and older clients.

## [68] - 2025-10-21
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
*/
package org.zaproxy.zap.extension.pscanrules;

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

private static final int PLUGIN_ID = 90011;

private static final Map<String, String> ALERT_TAGS;

static {
Expand All @@ -58,39 +62,49 @@ public class CharsetMismatchScanRule extends PluginPassiveScanner
ALERT_TAGS = Collections.unmodifiableMap(alertTags);
}

private static enum MismatchType {
NO_MISMATCH_METACONTENTTYPE_MISSING,
HEADER_METACONTENTYPE_MISMATCH,
HEADER_METACHARSET_MISMATCH,
METACONTENTTYPE_METACHARSET_MISMATCH,
XML_MISMATCH
private enum MismatchType {
HEADER_METACONTENTYPE_MISMATCH(
"-1",
"name.header_metacontentype_mismatch",
"extrainfo.html.header_metacontentype_mismatch"),
HEADER_METACHARSET_MISMATCH(
"-2",
"name.header_metacharset_mismatch",
"extrainfo.html.header_metacharset_mismatch"),
METACONTENTTYPE_METACHARSET_MISMATCH(
"-3",
"name.metacontenttype_metacharset_mismatch",
"extrainfo.html.metacontenttype_metacharset_mismatch"),
XML_MISMATCH("-4", "name", "extrainfo.xml");

private final String alertRef;
private final String name;
private final String otherInfoKey;

MismatchType(String ref, String nameKey, String otherInfoKey) {
this.alertRef = PLUGIN_ID + ref;
this.name = Constant.messages.getString(MESSAGE_PREFIX + nameKey);
this.otherInfoKey = MESSAGE_PREFIX + otherInfoKey;
}

String getAlertRef() {
return this.alertRef;
}

String getName() {
return this.name;
}

private String getExtraInfo(String firstCharset, String secondCharset) {
return Constant.messages.getString(otherInfoKey, firstCharset, secondCharset);
}
}

@Override
public String getName() {
return Constant.messages.getString(MESSAGE_PREFIX + "name");
}

public String getVariant(MismatchType currentType) {
switch (currentType) {
case NO_MISMATCH_METACONTENTTYPE_MISSING: // no_mismatch_metacontenttype_missing
return Constant.messages.getString(
MESSAGE_PREFIX + "variant.no_mismatch_metacontenttype_missing");
case HEADER_METACONTENTYPE_MISMATCH: // header_metacontentype_mismatch
return Constant.messages.getString(
MESSAGE_PREFIX + "variant.header_metacontentype_mismatch");
case HEADER_METACHARSET_MISMATCH: // header_metacharset_mismatch
return Constant.messages.getString(
MESSAGE_PREFIX + "variant.header_metacharset_mismatch");
case METACONTENTTYPE_METACHARSET_MISMATCH: // metacontenttype_metacharset_mismatch
return Constant.messages.getString(
MESSAGE_PREFIX + "variant.metacontenttype_metacharset_mismatch");
case XML_MISMATCH:
default:
return "";
}
}

@Override
public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
if (msg.getResponseBody().length() == 0) {
Expand Down Expand Up @@ -155,55 +169,31 @@ public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
// other
if (AlertThreshold.LOW.equals(pluginThreshold)
&& !bodyContentCharset.equalsIgnoreCase(metaCharset)) {
raiseAlert(
msg,
id,
metaCharset,
bodyContentCharset,
MismatchType
.METACONTENTTYPE_METACHARSET_MISMATCH); // body declarations
// inconsistent with
// each other
buildAlert(
metaCharset,
bodyContentCharset,
MismatchType.METACONTENTTYPE_METACHARSET_MISMATCH)
.raise();
}
}
if (hasBodyCharset) {
// Check the body content type charset declaration against the header
if (!bodyContentCharset.equalsIgnoreCase(headerCharset)) {
raiseAlert(
msg,
id,
headerCharset,
bodyContentCharset,
MismatchType.HEADER_METACONTENTYPE_MISMATCH); // body declaration
// doesn't match header
buildAlert(
headerCharset,
bodyContentCharset,
MismatchType.HEADER_METACONTENTYPE_MISMATCH)
.raise();
}
}
if (hasMetaCharset) {
// Check the body meta charset declaration against the header
if (!metaCharset.equalsIgnoreCase(headerCharset)) {
raiseAlert(
msg,
id,
headerCharset,
metaCharset,
MismatchType
.HEADER_METACHARSET_MISMATCH); // body declaration doesn't
// match header
}
// If Threshold is LOW be picky and report that
// only a meta charset declaration might be insufficient coverage for older
// clients
if (AlertThreshold.LOW.equals(pluginThreshold) && hasBodyCharset == false) {
raiseAlert(
msg,
id,
"",
"",
MismatchType
.NO_MISMATCH_METACONTENTTYPE_MISSING); // body declaration
// does match header
// but may overlook
// older clients
buildAlert(
headerCharset,
metaCharset,
MismatchType.HEADER_METACHARSET_MISMATCH)
.raise();
}
}
}
Expand All @@ -214,11 +204,11 @@ public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
// TODO: could there be more than one XML declaration tag for a single XML file?
List<StartTag> xmlDeclarationTags =
source.getAllStartTags(StartTagType.XML_DECLARATION);
if (xmlDeclarationTags.size() > 0) {
if (!xmlDeclarationTags.isEmpty()) {
StartTag xmlDeclarationTag = xmlDeclarationTags.get(0);
String encoding = xmlDeclarationTag.getAttributeValue("encoding");
if (!headerCharset.equalsIgnoreCase(encoding)) {
raiseAlert(msg, id, headerCharset, encoding, MismatchType.XML_MISMATCH);
buildAlert(headerCharset, encoding, MismatchType.XML_MISMATCH).raise();
}
}
}
Expand Down Expand Up @@ -261,33 +251,24 @@ private static String getBodyContentCharset(String bodyContentType) {
return charset;
}

private void raiseAlert(
HttpMessage msg,
int id,
String firstCharset,
String secondCharset,
MismatchType currentMismatch) {
newAlert()
.setName(
getName()
+ " "
+ getVariant(
currentMismatch)) // Compound name (to account for variant
// designations, and multiple alerts on single URI)
private AlertBuilder buildAlert(
String firstCharset, String secondCharset, MismatchType currentMismatch) {
return newAlert()
.setName(currentMismatch.getName())
.setRisk(getRisk())
.setConfidence(Alert.CONFIDENCE_LOW)
.setDescription(getDescription())
.setOtherInfo(getExtraInfo(firstCharset, secondCharset, currentMismatch))
.setOtherInfo(currentMismatch.getExtraInfo(firstCharset, secondCharset))
.setSolution(getSolution())
.setReference(getReference())
.setCweId(getCweId())
.setWascId(getWascId())
.raise();
.setAlertRef(currentMismatch.getAlertRef());
}

@Override
public int getPluginId() {
return 90011;
return PLUGIN_ID;
}

/*
Expand Down Expand Up @@ -323,46 +304,13 @@ public Map<String, String> getAlertTags() {
return ALERT_TAGS;
}

private static String getExtraInfo(
String firstCharset, String secondCharset, MismatchType mismatchType) {

String extraInfo = "";

switch (mismatchType) {
case NO_MISMATCH_METACONTENTTYPE_MISSING: // no_mismatch_metacontenttype_missing
extraInfo =
Constant.messages.getString(
MESSAGE_PREFIX
+ "extrainfo.html.no_mismatch_metacontenttype_missing");
break;
case HEADER_METACONTENTYPE_MISMATCH: // header_metacontentype_mismatch
extraInfo =
Constant.messages.getString(
MESSAGE_PREFIX + "extrainfo.html.header_metacontentype_mismatch",
firstCharset,
secondCharset);
break;
case HEADER_METACHARSET_MISMATCH: // header_metacharset_mismatch
extraInfo =
Constant.messages.getString(
MESSAGE_PREFIX + "extrainfo.html.header_metacharset_mismatch",
firstCharset,
secondCharset);
break;
case METACONTENTTYPE_METACHARSET_MISMATCH: // metacontenttype_metacharset_mismatch
extraInfo =
Constant.messages.getString(
MESSAGE_PREFIX
+ "extrainfo.html.metacontenttype_metacharset_mismatch",
firstCharset,
secondCharset);
break;
case XML_MISMATCH:
extraInfo =
Constant.messages.getString(
MESSAGE_PREFIX + "extrainfo.xml", firstCharset, secondCharset);
break;
}
return extraInfo;
@Override
public List<Alert> getExampleAlerts() {
return Arrays.stream(MismatchType.values())
.map(
mismatchType ->
buildAlert(StandardCharsets.UTF_8.name(), "ISO-123", mismatchType)
.build())
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ <H2 id="id-90011">Charset Mismatch</H2>
</li>
<li>Low Threshold:
<ul>
<li>Meta Content-Type Charset Missing - The response doesn't contain a META Content-Type declaration, which may overlook older clients.</li>
<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>
</ul>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,13 @@ pscanrules.charsetmismatch.desc = This check identifies responses where the HTTP
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.
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.
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.
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.
pscanrules.charsetmismatch.extrainfo.xml = There was a charset mismatch between the HTTP Header and the XML encoding declaration: [{0}] and [{1}] do not match.
pscanrules.charsetmismatch.name = Charset Mismatch
pscanrules.charsetmismatch.name.header_metacharset_mismatch = Charset Mismatch (Header Versus Meta Charset)
pscanrules.charsetmismatch.name.header_metacontentype_mismatch = Charset Mismatch (Header Versus Meta Content-Type Charset)
pscanrules.charsetmismatch.name.metacontenttype_metacharset_mismatch = Charset Mismatch (Meta Charset Versus Meta Content-Type Charset)
pscanrules.charsetmismatch.refs = https://code.google.com/p/browsersec/wiki/Part2#Character_set_handling_and_detection
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.
pscanrules.charsetmismatch.variant.header_metacharset_mismatch = (Header Versus Meta Charset)
pscanrules.charsetmismatch.variant.header_metacontentype_mismatch = (Header Versus Meta Content-Type Charset)
pscanrules.charsetmismatch.variant.metacontenttype_metacharset_mismatch = (Meta Charset Versus Meta Content-Type Charset)
pscanrules.charsetmismatch.variant.no_mismatch_metacontenttype_missing = (Meta Content-Type Charset Missing)

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.
pscanrules.contentsecuritypolicymissing.name = Content Security Policy (CSP) Header Not Set
Expand Down
Loading