diff --git a/docs/header_names/allowed_framework_names.adoc b/docs/header_names/allowed_framework_names.adoc
index d91acac3d7a..8bfffe474f5 100644
--- a/docs/header_names/allowed_framework_names.adoc
+++ b/docs/header_names/allowed_framework_names.adoc
@@ -54,6 +54,7 @@
* Jdom2
* JSP
* Legacy Mongo Java API
+* NanoHTTPD
* OkHttp
* OpenAI
* Realm
diff --git a/rules/S5131/java/how-to-fix-it/nanohttpd.adoc b/rules/S5131/java/how-to-fix-it/nanohttpd.adoc
new file mode 100644
index 00000000000..220b2a242d7
--- /dev/null
+++ b/rules/S5131/java/how-to-fix-it/nanohttpd.adoc
@@ -0,0 +1,106 @@
+== How to fix it in a NanoHTTPD
+
+=== Code examples
+
+The following code is vulnerable to cross-site scripting because it returns an HTML response that contains user input.
+
+Third-party data, such as user input, is not to be trusted. If embedded in HTML code, it should be HTML-encoded to prevent the injection of additional code.
+This can be done with the https://owasp.org/www-project-java-encoder/[OWASP Java Encoder] or similar libraries.
+
+==== Noncompliant code example
+
+[source,java,diff-id=41,diff-type=noncompliant]
+----
+import fi.iki.elonen.NanoHTTPD;
+
+public class App extends NanoHTTPD {
+
+ @Override
+ public Response serve(IHTTPSession session) {
+ String name = session.getParms().get("input");
+ return newFixedLengthResponse( // Noncompliant
+ "
Hello, " + name + "!
"
+ );
+ }
+}
+----
+
+==== Compliant solution
+
+[source,java,diff-id=41,diff-type=compliant]
+----
+import fi.iki.elonen.NanoHTTPD;
+import org.owasp.encoder.Encode;
+
+public class App extends NanoHTTPD {
+
+ @Override
+ public Response serve(IHTTPSession session) {
+ String name = session.getParms().get("input");
+ return newFixedLengthResponse(
+ " Hello, " + Encode.forHtml(name) + "!
"
+ );
+ }
+}
+----
+
+If you do not intend to send HTML code to clients, the vulnerability can be fixed by specifying the type of data returned in the response with the Content-Type header.
+
+For example, setting the Content-Type to `text/plain` allows to safely reflect user input.
+In this case, browsers will not try to parse and execute the response.
+
+==== Noncompliant code example
+
+[source,java,diff-id=42,diff-type=noncompliant]
+----
+import fi.iki.elonen.NanoHTTPD;
+
+public class App extends NanoHTTPD {
+
+ @Override
+ public Response serve(IHTTPSession session) {
+ String name = session.getParms().get("input");
+ return newFixedLengthResponse( // Noncompliant - text/html by default
+ "Hello, " + name + "!"
+ );
+ }
+}
+----
+
+==== Compliant solution
+
+[source,java,diff-id=42,diff-type=compliant]
+----
+import fi.iki.elonen.NanoHTTPD;
+import fi.iki.elonen.NanoHTTPD.Response.Status;
+
+public class App extends NanoHTTPD {
+
+ @Override
+ public Response serve(IHTTPSession session) {
+ String name = session.getParms().get("input");
+ return newFixedLengthResponse(
+ Status.OK,
+ "text/plain",
+ "Hello, " + name + "!"
+ );
+ }
+}
+----
+
+=== How does this work?
+
+include::../../common/fix/data_encoding.adoc[]
+
+`org.owasp.encoder.Encode.forHtml` is the recommended method to encode HTML entities.
+
+=== Pitfalls
+
+include::../../common/pitfalls/content-types.adoc[]
+
+include::../../common/pitfalls/validation.adoc[]
+
+=== Going the extra mile
+
+include::../../common/extra-mile/csp.adoc[]
+
diff --git a/rules/S5131/java/rule.adoc b/rules/S5131/java/rule.adoc
index d192e851f30..3196ef6e983 100644
--- a/rules/S5131/java/rule.adoc
+++ b/rules/S5131/java/rule.adoc
@@ -16,6 +16,8 @@ include::how-to-fix-it/spring.adoc[]
include::how-to-fix-it/thymeleaf.adoc[]
+include::how-to-fix-it/nanohttpd.adoc[]
+
== Resources
include::../common/resources/docs.adoc[]
diff --git a/rules/S5131/kotlin/how-to-fix-it/nanohttpd.adoc b/rules/S5131/kotlin/how-to-fix-it/nanohttpd.adoc
new file mode 100644
index 00000000000..a24e95daa5c
--- /dev/null
+++ b/rules/S5131/kotlin/how-to-fix-it/nanohttpd.adoc
@@ -0,0 +1,106 @@
+== How to fix it in a NanoHTTPD
+
+=== Code examples
+
+The following code is vulnerable to cross-site scripting because it returns an HTML response that contains user input.
+
+Third-party data, such as user input, is not to be trusted. If embedded in HTML code, it should be HTML-encoded to prevent the injection of additional code.
+This can be done with the https://owasp.org/www-project-java-encoder/[OWASP Java Encoder] or similar libraries.
+
+==== Noncompliant code example
+
+[source,kotlin,diff-id=41,diff-type=noncompliant]
+----
+import fi.iki.elonen.NanoHTTPD
+
+class App : NanoHTTPD() {
+
+ @Override
+ fun serve(session: IHTTPSession): Response {
+ val name = session.getParms().get("input");
+ return newFixedLengthResponse( // Noncompliant
+ " Hello, $name!
"
+ );
+ }
+}
+----
+
+==== Compliant solution
+
+[source,kotlin,diff-id=41,diff-type=compliant]
+----
+import fi.iki.elonen.NanoHTTPD
+import org.owasp.encoder.Encode
+
+class App : NanoHTTPD() {
+
+ @Override
+ fun serve(session: IHTTPSession): Response {
+ val name = session.getParms().get("input");
+ return newFixedLengthResponse(
+ " Hello, ${Encode.forHtml(name)}!
"
+ );
+ }
+}
+----
+
+If you do not intend to send HTML code to clients, the vulnerability can be fixed by specifying the type of data returned in the response with the Content-Type header.
+
+For example, setting the Content-Type to `text/plain` allows to safely reflect user input.
+In this case, browsers will not try to parse and execute the response.
+
+==== Noncompliant code example
+
+[source,kotlin,diff-id=42,diff-type=noncompliant]
+----
+import fi.iki.elonen.NanoHTTPD
+
+class App : NanoHTTPD() {
+
+ @Override
+ fun serve(session: IHTTPSession): Response {
+ val name = session.getParms().get("input");
+ return newFixedLengthResponse( // Noncompliant - text/html by default
+ "Hello, $name!"
+ );
+ }
+}
+----
+
+==== Compliant solution
+
+[source,kotlin,diff-id=42,diff-type=compliant]
+----
+import fi.iki.elonen.NanoHTTPD
+import fi.iki.elonen.NanoHTTPD.Response.Status
+
+class App : NanoHTTPD() {
+
+ @Override
+ fun serve(session: IHTTPSession): Response {
+ val name = session.getParms().get("input");
+ return newFixedLengthResponse(
+ Status.OK,
+ "text/plain",
+ "Hello, $name!"
+ );
+ }
+}
+----
+
+=== How does this work?
+
+include::../../common/fix/data_encoding.adoc[]
+
+`org.owasp.encoder.Encode.forHtml` is the recommended method to encode HTML entities.
+
+=== Pitfalls
+
+include::../../common/pitfalls/content-types.adoc[]
+
+include::../../common/pitfalls/validation.adoc[]
+
+=== Going the extra mile
+
+include::../../common/extra-mile/csp.adoc[]
+
diff --git a/rules/S5131/kotlin/rule.adoc b/rules/S5131/kotlin/rule.adoc
index dd0abf95fae..ca679d04bfe 100644
--- a/rules/S5131/kotlin/rule.adoc
+++ b/rules/S5131/kotlin/rule.adoc
@@ -12,6 +12,8 @@ include::how-to-fix-it/servlet.adoc[]
include::how-to-fix-it/spring.adoc[]
+include::how-to-fix-it/nanohttpd.adoc[]
+
== Resources
include::../common/resources/docs.adoc[]