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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ unchanged. [1.5.0 is available from Central](releases/1.5.0-central-publication.

## Unreleased

- Clarify JavaScript encoding modes, output growth, and insertion-boundary
requirements for #243; distinguish JSON whole-string HTML embedding from
JavaScript fragment protection. Correct internal HTML/XML comments and
strengthen parser tests to verify script text, not only absence of a marker.
Encoding behavior is unchanged.
- Resume development at `1.5.1-SNAPSHOT`, with the immutable 1.5.0 public-API
baseline and its matching support APIs. Minimum-consumer fixtures are unchanged.
- Discover release tags independently of commit ancestry so squash merges cannot
Expand Down Expand Up @@ -42,7 +47,7 @@ Signed artifact availability and independent verification are tracked in the
* build/compatibility: update the published JSP provided API to 2.3.3 and the Jakarta test classpath to Servlet 6.1.0 and EL 6.0.1, while retaining independent JSP 2.2.1 and Java 8-compatible Jakarta minimum-consumer fixtures. Japicmp now resolves distinct old/new support classpaths so the 1.4.1 comparison remains complete. Dependabot scans the root Maven reactor once, rather than opening duplicate module proposals, and continues to scan the standalone compatibility-fixture project separately.
* feat: all four `forJavaScript*` methods encode dollar sign (`$`) as `\x24`, backtick as `\x60`, and opening brace (`{`) as `\x7b` [#129](https://github.com/OWASP/owasp-java-encoder/issues/129). Escaping `{` prevents input after a trusted `$` from completing `${...}`. Encoded output now supports literal text in ordinary (untagged) template literals as well as single- and double-quoted strings. This changes the encoded output while preserving its decoded JavaScript string value. Tagged templates (including `String.raw`), `${...}` expression bodies, JSON, and script URLs are unsupported; each method's HTML context restrictions still apply.
* fix: all four `forJavaScript*` methods escape unpaired UTF-16 surrogates as `\uXXXX`, preserving their JavaScript string values through UTF-8 serialization [#135](https://github.com/OWASP/owasp-java-encoder/issues/135), and escape DEL/C1 controls (U+007F to U+009F) as `\xNN` [#163](https://github.com/OWASP/owasp-java-encoder/issues/163). Valid surrogate pairs and other non-ASCII text remain unescaped except U+2028/U+2029. These are output-fidelity changes; NEL was already ordinary JavaScript string data.
* fix: the HTML/block JavaScript encoders escape every ASCII character that can contribute to a case-insensitive `</script` end tag or the `<!--` and `-->` script tokens, including the HTML end-tag delimiters, so any nonempty encoded substring cannot complete a delimiter supplied partly by adjacent trusted literal text. `forCDATA` represents every `]` and `>` with close/reopen sequences, preserving parsed text while breaking every nonempty encoded substring of `]]>`; its String facade grows with actual output instead of eagerly reserving the 13× maximum. `forXmlComment` replaces every hyphen with `~`. These are substantial compatibility-visible output changes; see the [migration record](docs/compatibility-decisions.md#15-parser-boundary-output-migration).
* fix: the HTML/block JavaScript encoders escape every ASCII character that can contribute to case-insensitive `</script` or `<script` tokens or the `<!--` and `-->` script tokens, including their delimiters, so the encoding of any nonempty value cannot complete a delimiter supplied partly by adjacent trusted literal text. `forCDATA` represents every `]` and `>` with close/reopen sequences, preserving parsed text while breaking every nonempty encoded substring of `]]>`; its String facade grows with actual output instead of eagerly reserving the 13× maximum. `forXmlComment` replaces every hyphen with `~`. These are substantial compatibility-visible output changes; see the [migration record](docs/compatibility-decisions.md#15-parser-boundary-output-migration).
* fix: `EncodedWriter` now enforces Writer lifecycle semantics: write, append and flush operations fail after close; repeated close is harmless; the first close finalizes pending input and still attempts the delegate close, preserving simultaneous failures with suppressed exceptions. Array-slice writes now use overflow-safe bounds validation, including `Integer.MAX_VALUE`-shaped ranges.
* build: compare all three 1.5.0 artifacts against the immutable 1.4.1 public-API baseline, and verify that every publishable effective POM inherits repository-root SCM connection, developer connection and URL values without module-name suffixes.
* feat: add `Encode.forJson` String/Writer methods, the `json` encoder context, and `forJson` tags and EL functions in both JSP and Jakarta tag libraries [#145](https://github.com/OWASP/owasp-java-encoder/issues/145). The caller supplies double quotes. Output uses RFC 8259 string escapes and also escapes HTML script delimiters. Java `null` becomes the text `null` (the JSON string `"null"` when quoted); unpaired surrogates use Unicode escapes and may not interoperate with every JSON consumer. Prefer a serializer for complete JSON documents.
Expand Down
114 changes: 82 additions & 32 deletions core/src/main/java/org/owasp/encoder/Encode.java
Original file line number Diff line number Diff line change
Expand Up @@ -1103,14 +1103,47 @@ public static void forJava(Writer out, String input)
* blocks, and JavaScript source. The caller MUST
* provide surrounding single (') or double (") quotation marks, or backticks
* (`) for an ordinary (untagged) template literal.
* Since this performs additional encoding so it can work in all
* of the JavaScript contexts listed, it may be slightly less
* efficient than using one of the methods targeted to a specific
* JavaScript context ({@link #forJavaScriptAttribute(String)},
* {@link #forJavaScriptBlock}, {@link #forJavaScriptSource}).
* Unless you are interested in saving a few bytes of output or
* are writing a framework on top of this library, it is recommend
* that you use this method over the others.</p>
* HTML event-attribute values must also be quoted at the HTML level.
* This general method applies the protections needed for all of these
* contexts. The resulting source can be substantially longer than the input;
* the evaluated JavaScript string value is preserved, not its source spelling.
* Choose {@link #forJavaScriptAttribute(String)},
* {@link #forJavaScriptBlock(String)}, or {@link #forJavaScriptSource(String)}
* when the output context is known and satisfies that method's contract.</p>
*
* <p><b>Insertion boundaries:</b> insert the complete encoded result into
* literal string text, not into a JavaScript expression or where it can
* continue or extend an escape sequence. Trusted text immediately before the
* insertion must not leave an unmatched backslash or an incomplete hex or
* Unicode escape, or an extendable escape such as <code>\0</code> before
* an input digit. In an HTML event attribute, it must not continue an HTML
* character reference begun by trusted text, such as an unencoded
* <code>&amp;</code> followed by encoded <code>#39;</code>. Encode the
* complete string value, including trusted text, when possible.
* The surrounding template must keep the insertion in the
* intended JavaScript string and HTML context. Encoding cannot repair an
* already unsafe template, such as an HTML script end-tag name
* <code>&lt;/script</code> followed by whitespace or <code>/</code>
* before the insertion, with the closing <code>&gt;</code> supplied later.</p>
*
* <p>In HTML script blocks, a nonempty encoded fragment cannot complete the
* raw sequences <code>&lt;/script</code> or <code>&lt;script</code>
* (case-insensitive),
* <code>&lt;!--</code>, or <code>--&gt;</code> with adjacent trusted text.
* For example, trusted <code>&lt;/scr</code>, an encoded <code>i</code>, and
* trusted <code>pt&gt;</code> cannot form an HTML closing script tag.
* Preventing a split <code>&lt;script</code> also avoids entering the HTML
* tokenizer's double-escaped state after a trusted <code>&lt;!--</code>,
* which could otherwise cause the rest of the page to be consumed as script.
* Empty input produces empty output and cannot separate dangerous trusted
* fragments. These protections do not sanitize the surrounding template.</p>
*
* <p>For example, <code>forJavaScript("http://google.com")</code> returns
* <code>h\x74\x74\x70:\/\/google.\x63om</code>, which evaluates to the
* original URL inside a supported JavaScript string literal.
* {@link #forJavaScriptSource(String)} and
* {@link #forJavaScriptAttribute(String)} leave this URL unchanged, but
* must only be used in their respective output contexts.</p>
*
* <b>Example JSP Usage:</b>
* <pre>
Expand Down Expand Up @@ -1195,9 +1228,8 @@ public static void forJava(Writer out, String input)
* <tr class="rowColor">
* <td class="colFirst">U+002F</td><td><code>/</code></td>
* <td class="colLast"><code>\/</code></td>
* <td class="colLast">This encoding is used to avoid an input sequence
* "&lt;/" from prematurely terminating a &lt;/script&gt;
* block.</td>
* <td class="colLast">Escaping slash prevents it from contributing to
* an HTML script end tag such as <code>&lt;/script&gt;</code>.</td>
* </tr>
* <tr class="altColor">
* <td class="colFirst">U+003C</td><td><code>&lt;</code></td>
Expand All @@ -1212,8 +1244,10 @@ public static void forJava(Writer out, String input)
* <tr class="altColor">
* <td class="colFirst" colspan="2">C, I, P, R, S, T and lowercase</td>
* <td class="colLast"><code>\x##</code></td>
* <td class="colLast">Breaks every nonempty encoded substring of a
* case-insensitive <code>&lt;/script</code> token.</td>
* <td class="colLast">Prevents a nonempty encoded fragment from completing
* the case-insensitive sequences <code>&lt;/script</code> or
* <code>&lt;script</code> with
* adjacent trusted text; see the insertion-boundary requirements above.</td>
* </tr>
* <tr class="rowColor">
* <td class="colFirst">U+005C</td><td><code>\</code></td>
Expand All @@ -1236,7 +1270,7 @@ public static void forJava(Writer out, String input)
* <td class="colFirst" colspan="2">U+0000&nbsp;to&nbsp;U+001F</td>
* <td class="colLast"><code>\x##</code></td>
* <td class="colLast">Hexadecimal encoding is used for characters in this
* range that were not already mentioned in above.</td>
* range that were not already listed above.</td>
* </tr>
* <tr class="altColor">
* <td class="colFirst">U+002D</td><td><code>-</code></td>
Expand Down Expand Up @@ -1271,7 +1305,9 @@ public static void forJava(Writer out, String input)
* preserve the string value in single-quoted, double-quoted, and ordinary
* template literals. Tagged templates (including {@code String.raw}) are not
* supported: tags can observe raw escapes or interpret the text in another
* language. This is not a JSON encoder; use a JSON serializer for JSON.
* language. This is not a JSON encoder; use a JSON serializer for complete
* JSON documents. JSON embedded in HTML must also be safe for that HTML
* context; see {@link #forJson(String)} for JSON string contents.
* Do not use the result in {@code javascript:} or {@code data:} URLs;
* URL decoding introduces a separate parsing context.</p>
*
Expand Down Expand Up @@ -1312,10 +1348,10 @@ public static void forJavaScript(Writer out, String input)
* <p>DEL and C1 controls (U+007F to U+009F) are hex-escaped. Unpaired UTF-16
* surrogates use Unicode escapes; valid surrogate pairs remain unescaped.</p>
*
* <p><strong>Unless you are interested in saving a few bytes of
* output or are writing a framework on top of this library, it is
* recommend that you use {@link #forJavaScript(String)} over this
* method.</strong></p>
* <p>The HTML attribute value must also be enclosed in quotation marks.
* The insertion-boundary requirements of {@link #forJavaScript(String)}
* apply. This method avoids the additional HTML script-block escapes,
* which can substantially increase source size.</p>
*
* <b>Example JSP Usage:</b>
* <pre>
Expand All @@ -1325,7 +1361,8 @@ public static void forJavaScript(Writer out, String input)
* <p>For ordinary template literals, insert the result into literal text,
* not a <code>${...}</code> expression. Tagged templates (including
* {@code String.raw}) are not supported; see {@link #forJavaScript(String)}.
* This is not a JSON encoder; use a JSON serializer for JSON.
* This is not a JSON encoder; use a JSON serializer for complete JSON
* documents. For JSON embedded in HTML, see {@link #forJson(String)}.
* Do not use the result in {@code javascript:} or {@code data:} URLs;
* URL decoding introduces a separate parsing context.</p>
*
Expand Down Expand Up @@ -1366,10 +1403,10 @@ public static void forJavaScriptAttribute(Writer out, String input)
* <p>DEL and C1 controls (U+007F to U+009F) are hex-escaped. Unpaired UTF-16
* surrogates use Unicode escapes; valid surrogate pairs remain unescaped.</p>
*
* <p><strong>Unless you are interested in saving a few bytes of
* output or are writing a framework on top of this library, it is
* recommend that you use {@link #forJavaScript(String)} over this
* method.</strong></p>
* <p>The insertion-boundary requirements and nonempty-fragment HTML
* protection of {@link #forJavaScript(String)} apply. This method retains
* the script-letter escapes; its shorter quote escapes do not make it safe
* for HTML event attributes.</p>
*
* <b>Example JSP Usage:</b>
* <pre>
Expand All @@ -1381,7 +1418,8 @@ public static void forJavaScriptAttribute(Writer out, String input)
* <p>For ordinary template literals, insert the result into literal text,
* not a <code>${...}</code> expression. Tagged templates (including
* {@code String.raw}) are not supported; see {@link #forJavaScript(String)}.
* This is not a JSON encoder; use a JSON serializer for JSON.
* This is not a JSON encoder; use a JSON serializer for complete JSON
* documents. For JSON embedded in HTML, see {@link #forJson(String)}.
* Do not use the result in {@code javascript:} or {@code data:} URLs;
* URL decoding introduces a separate parsing context.</p>
*
Expand Down Expand Up @@ -1422,10 +1460,10 @@ public static void forJavaScriptBlock(Writer out, String input)
* <p>DEL and C1 controls (U+007F to U+009F) are hex-escaped. Unpaired UTF-16
* surrogates use Unicode escapes; valid surrogate pairs remain unescaped.</p>
*
* <p><strong>Unless you are interested in saving a few bytes of
* output or are writing a framework on top of this library, it is
* recommend that you use {@link #forJavaScript(String)} over this
* method.</strong></p>
* <p>The JavaScript insertion-boundary requirements of
* {@link #forJavaScript(String)} apply. This method avoids HTML-specific
* escaping and leaves <code>http://google.com</code> unchanged. Use it only
* for standalone JavaScript resources, not to reduce escaping in HTML.</p>
*
* <b>Example JSP Usage:</b>
* This example is serving up JavaScript source directly:
Expand All @@ -1437,7 +1475,8 @@ public static void forJavaScriptBlock(Writer out, String input)
* <p>For ordinary template literals, insert the result into literal text,
* not a <code>${...}</code> expression. Tagged templates (including
* {@code String.raw}) are not supported; see {@link #forJavaScript(String)}.
* This is not a JSON encoder; use a JSON serializer for JSON.
* This is not a JSON encoder; use a JSON serializer for complete JSON
* documents. For JSON embedded in HTML, see {@link #forJson(String)}.
* Do not use the result in {@code javascript:} or {@code data:} URLs;
* URL decoding introduces a separate parsing context.</p>
*
Expand Down Expand Up @@ -1480,6 +1519,16 @@ public static void forJavaScriptSource(Writer out, String input)
* <code>&gt;</code> or <code>&amp;</code>.</li>
* </ul>
*
* <p>For HTML script embedding, encode the complete contents of each JSON
* string value and member name, including any trusted text that belongs
* inside that string, and then supply its double quotes. Do not insert the
* result into an unfinished JSON escape sequence. This method does not
* protect arbitrary fragments joined to unencoded string text: for example,
* trusted <code>&lt;/scr</code> + <code>forJson("i")</code> + trusted
* <code>pt&gt;</code> still forms an HTML closing script tag. The rest of
* the JSON document and enclosing template must also be safe for the HTML
* context.</p>
*
* <p>The encoding is:</p>
*
* <ul>
Expand All @@ -1490,8 +1539,9 @@ public static void forJavaScriptSource(Writer out, String input)
* <code>\f</code>, <code>\r</code> or <code>&#92;u00xx</code>.</li>
* <li><code>&lt;</code>, <code>&gt;</code> and <code>&amp;</code>
* are encoded as <code>&#92;u003c</code>, <code>&#92;u003e</code> and
* <code>&#92;u0026</code>, so the output cannot close or comment out an
* enclosing HTML <code>&lt;script&gt;</code> element.</li>
* <code>&#92;u0026</code>, so the complete encoded string content cannot
* supply an HTML script end tag or comment token. The surrounding text
* remains subject to the requirements above.</li>
* <li>U+2028 and U+2029 are encoded as <code>&#92;u2028</code> and
* <code>&#92;u2029</code>.</li>
* <li>Unpaired surrogates are encoded as <code>&#92;ud800</code> to
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/owasp/encoder/Encoders.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public final class Encoders {
static final JavaScriptEncoder JAVASCRIPT_ENCODER
= map(JAVASCRIPT, new JavaScriptEncoder(JavaScriptEncoder.Mode.HTML, false));
/**
* Encoder for JavaScript appearing in XML/HTML attributes.
* Encoder for JavaScript string literal text in quoted HTML event attributes.
*/
static final JavaScriptEncoder JAVASCRIPT_ATTRIBUTE_ENCODER
= map(JAVASCRIPT_ATTRIBUTE, new JavaScriptEncoder(JavaScriptEncoder.Mode.ATTRIBUTE, false));
Expand Down
Loading
Loading