Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ private Element handleImageLink( final String reallink, final String link, final

private Element handleAccessRule( String ruleLine ) {
if( m_wysiwygEditorMode ) {
m_currentElement.addContent( "[" + ruleLine + "]" );
m_currentElement.addContent( "[" + TextUtil.escapeHTMLEntities( ruleLine ) + "]" );
}
if( !m_parseAccessRules ) {
return m_currentElement;
Expand Down Expand Up @@ -821,7 +821,7 @@ private Element handleAccessRule( String ruleLine ) {
*/
private Element handleMetadata( final String link ) {
if( m_wysiwygEditorMode ) {
m_currentElement.addContent( "[" + link + "]" );
m_currentElement.addContent( "[" + TextUtil.escapeHTMLEntities( link ) + "]" );
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import org.apache.wiki.api.plugin.PluginElement;
import org.apache.wiki.plugin.PluginManager;
import org.apache.wiki.preferences.Preferences;
import org.apache.wiki.util.TextUtil;
import org.apache.wiki.variables.VariableManager;
import org.jdom2.Text;

Expand Down Expand Up @@ -147,8 +148,10 @@ public String invoke( final Context context ) {
if( wysiwygEditorMode && !m_pluginName.matches( EMITTABLE_PLUGINS ) ) {
result = PLUGIN_START + m_pluginName + SPACE;

// convert newlines to <br> in case the plugin has a body.
final String cmdLine = m_params.get( CMDLINE ).replaceAll( LINEBREAK, ELEMENT_BR );
// escape the raw command line first: this text node is emitted with output escaping
// disabled, so unescaped markup would reach the wysiwyg editor (and its <textarea>)
// as live HTML. Then convert newlines to <br> in case the plugin has a body.
final String cmdLine = TextUtil.escapeHTMLEntities( m_params.get( CMDLINE ) ).replaceAll( LINEBREAK, ELEMENT_BR );
result = result + cmdLine + PLUGIN_END;
} else {
final Boolean b = context.getVariable( Context.VAR_EXECUTE_PLUGINS );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,15 @@ public void tearDown() {
}

private String render( final String s ) throws IOException {
return render( s, false );
}

private String render( final String s, final boolean wysiwygEditorMode ) throws IOException {
final Page dummyPage = Wiki.contents().page(testEngine,"TestPage");
final Context ctx = Wiki.context().create(testEngine,dummyPage);
if( wysiwygEditorMode ) {
ctx.setVariable( Context.VAR_WYSIWYG_EDITOR_MODE, Boolean.TRUE );
}

final StringReader in = new StringReader(s);

Expand Down Expand Up @@ -92,4 +99,23 @@ public void testUndefinedPageLink() throws Exception {
Assertions.assertEquals( "<a class=\"createpage\" href=\"Non-existent Pagename with Spaces\">Non-existent Pagename with Spaces</a>", render(src) );
}

/**
* The wysiwyg render path echoes raw plugin command lines, access rules and metadata back into the
* generated HTML with output escaping disabled. Unescaped markup there breaks out of the wysiwyg
* editors' &lt;textarea&gt; (stored XSS). See the textarea sinks in templates/editors/*.jsp.
*/
@Test
public void testWysiwygEchoedMarkupIsEscaped() throws Exception {
final String payload = "</textarea><script>alert(1)</script>";

String html = render( "[{SomePlugin " + payload + "}]", true );
Assertions.assertFalse( html.contains( payload ), html );

html = render( "[{SET foo='" + payload + "'}]", true );
Assertions.assertFalse( html.contains( payload ), html );

html = render( "[{ALLOW edit " + payload + "}]", true );
Assertions.assertFalse( html.contains( payload ), html );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
</fieldset>
</wiki:CheckRequestContext>

<textarea name="htmlPageText"><%=pageAsHtml%></textarea>
<textarea name="htmlPageText"><%=pageAsHtml.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")%></textarea>
</form>
<script type="text/javascript">
//<![CDATA[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
</fieldset>
</wiki:CheckRequestContext>

<textarea name="htmlPageText"><%=pageAsHtml%></textarea>
<textarea name="htmlPageText"><%=pageAsHtml.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")%></textarea>
</form>
<script type="text/javascript">
//<![CDATA[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@
<div class="row edit-area livepreview previewcolumn"><%-- .livepreview .previewcolumn--%>
<div>
<textarea name="htmlPageText"
autofocus="autofocus"><%=pageAsHtml.replace("&", "&amp;")%></textarea>
autofocus="autofocus"><%=pageAsHtml.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")%></textarea>
</div>
<div class="ajaxpreview" >Preview comes here</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@
<div class="row edit-area livepreview previewcolumn"><%-- .livepreview .previewcolumn--%>
<div>
<textarea name="htmlPageText"
autofocus="autofocus"><%=pageAsHtml.replace("&", "&amp;")%></textarea>
autofocus="autofocus"><%=pageAsHtml.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")%></textarea>
</div>
<div class="ajaxpreview" >Preview comes here</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@
To avoid this, double escape the & char => so &amp;lt; is converted to &lt;
--%>
<textarea name="htmlPageText"
autofocus="autofocus"><%= pageAsHtml.replace("&", "&amp;")%></textarea>
autofocus="autofocus"><%= pageAsHtml.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")%></textarea>
</div>
<div class="ajaxpreview">Preview comes here</div>
</div>
Expand Down
Loading