Browse Source

post-process generated javadocs as workaround for CVE-2013-1571 - based on Maven patch by Uwe Schindler - PR 55132

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1496083 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 12 years ago
parent
commit
7bc745a289
6 changed files with 171 additions and 3 deletions
  1. +1
    -0
      CONTRIBUTORS
  2. +9
    -0
      WHATSNEW
  3. +4
    -0
      contributors.xml
  4. +12
    -0
      manual/Tasks/javadoc.html
  5. +108
    -3
      src/main/org/apache/tools/ant/taskdefs/Javadoc.java
  6. +37
    -0
      src/resources/org/apache/tools/ant/taskdefs/javadoc-frame-injections-fix.txt

+ 1
- 0
CONTRIBUTORS View File

@@ -364,6 +364,7 @@ Tom May
Tomasz Bech
Trejkaz Xaoza
Ulrich Schmidt
Uwe Schindler
Valentino Miazzo
Victor Toni
Vimil Saju


+ 9
- 0
WHATSNEW View File

@@ -23,6 +23,15 @@ Fixed bugs:
Other changes:
--------------

* <javadoc> will now post-process the generated in order to mitigate
the frame injection attack possible in javadocs generated by Oracle
JDKs prior to Java7 Update 25. The vulnerability is known as
CVE-2013-1571.
There is an option to turn off the post-processing but it is only
recommended you do so if all your builds use a JDK that's not
vulnerable.
Bugzilla Report 55132.

Changes from Ant 1.9.0 TO Ant 1.9.1
===================================



+ 4
- 0
contributors.xml View File

@@ -1460,6 +1460,10 @@
<first>Ulrich</first>
<last>Schmidt</last>
</name>
<name>
<first>Uwe</first>
<last>Schindler</last>
</name>
<name>
<first>Valentino</first>
<last>Miazzo</last>


+ 12
- 0
manual/Tasks/javadoc.html View File

@@ -509,6 +509,18 @@ to &lt;javadoc&gt; using <tt>classpath</tt>, <tt>classpathref</tt> attributes or
<td align="center" valign="top">1.4</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">postProcessGeneratedJavadocs</td>
<td valign="top">Whether to post-process the generated javadocs in
order to mitigate CVE-2013-1571. Defaults to true. <em>Since Ant
1.9.2</em><br></td>
There is a frame injection attack possible in javadocs generated by Oracle
JDKs prior to Java7 Update 25. When this flag is set to true, Ant
will check whether the docs are vulnerable and will try to fix them.
</td>
<td align="center" valign="top">1.4</td>
<td align="center" valign="top">No</td>
</tr>
</table>

<h4><a name="groupattribute">Format of the group attribute</a></h4>


+ 108
- 3
src/main/org/apache/tools/ant/taskdefs/Javadoc.java View File

@@ -17,13 +17,20 @@
*/
package org.apache.tools.ant.taskdefs;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -50,6 +57,7 @@ import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.FileProvider;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.StringUtils;
import org.apache.tools.ant.util.JavaEnvUtils;

/**
@@ -81,6 +89,9 @@ public class Javadoc extends Task {
private static final boolean JAVADOC_5 =
!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4);

private static final String LOAD_FRAME = "function loadFrames() {";
private static final int LOAD_FRAME_LEN = LOAD_FRAME.length();

/**
* Inner class used to manage doclet parameters.
*/
@@ -452,6 +463,8 @@ public class Javadoc extends Task {
private String executable = null;
private boolean docFilesSubDirs = false;
private String excludeDocFilesSubDir = null;
private String docEncoding = null;
private boolean postProcessGeneratedJavadocs = true;

private ResourceCollectionContainer nestedSourceFiles
= new ResourceCollectionContainer();
@@ -1151,6 +1164,7 @@ public class Javadoc extends Task {
public void setDocencoding(String enc) {
cmd.createArgument().setValue("-docencoding");
cmd.createArgument().setValue(enc);
docEncoding = enc;
}

/**
@@ -1655,6 +1669,14 @@ public class Javadoc extends Task {
excludeDocFilesSubDir = s;
}

/**
* Whether to post-process the generated javadocs in order to mitigate CVE-2013-1571.
* @since Ant 1.9.2
*/
public void setPostProcessGeneratedJavadocs(boolean b) {
postProcessGeneratedJavadocs = b;
}

/**
* Execute the task.
* @throws BuildException on error
@@ -1765,6 +1787,7 @@ public class Javadoc extends Task {
throw new BuildException("Javadoc returned " + ret,
getLocation());
}
postProcessGeneratedJavadocs();
} catch (IOException e) {
throw new BuildException("Javadoc failed: " + e, e, getLocation());
} finally {
@@ -2420,6 +2443,88 @@ public class Javadoc extends Task {
}
}

private void postProcessGeneratedJavadocs() throws IOException {
if (!postProcessGeneratedJavadocs) {
return;
}
final String fixData;
final InputStream in = getClass()
.getResourceAsStream("javadoc-frame-injections-fix.txt");
if (in == null) {
throw new FileNotFoundException("Missing resource "
+ "'javadoc-frame-injections-fix.txt' in "
+ "classpath.");
}
try {
fixData = FileUtils.readFully(new InputStreamReader(in, "US-ASCII")).trim()
.replace("\r\n", StringUtils.LINE_SEP)
.replace("\n", StringUtils.LINE_SEP);
} finally {
FileUtils.close(in);
}

final DirectoryScanner ds = new DirectoryScanner();
ds.setBasedir(destDir);
ds.setCaseSensitive(false);
ds.setIncludes(new String[] {
"**/index.html", "**/index.htm", "**/toc.html", "**/toc.htm"
});
ds.addDefaultExcludes();
ds.scan();
int patched = 0;
for (String f : ds.getIncludedFiles()) {
patched += postProcess(new File(destDir, f), fixData);
}
if (patched > 0) {
log("Patched " + patched + " link injection vulnerable javadocs",
Project.MSG_INFO);
}
}

private int postProcess(File file, String fixData) throws IOException {
String enc = docEncoding != null ? docEncoding
: FILE_UTILS.getDefaultEncoding();
// we load the whole file as one String (toc/index files are
// generally small, because they only contain frameset declaration):
InputStream fin = new FileInputStream(file);
String fileContents;
try {
fileContents =
FileUtils.safeReadFully(new InputStreamReader(fin, enc));
} finally {
FileUtils.close(fin);
}

// check if file may be vulnerable because it was not
// patched with "validURL(url)":
if (fileContents.indexOf("function validURL(url) {") < 0) {
// we need to patch the file!
String patchedFileContents = patchContent(fileContents, fixData);
if (!patchedFileContents.equals(fileContents)) {
FileOutputStream fos = new FileOutputStream(file);
try {
OutputStreamWriter w = new OutputStreamWriter(fos, enc);
w.write(patchedFileContents);
w.close();
return 1;
} finally {
FileUtils.close(fos);
}
}
}
return 0;
}

private String patchContent(String fileContents, String fixData) {
// using regexes here looks like overkill
int start = fileContents.indexOf(LOAD_FRAME);
if (start >= 0) {
return fileContents.substring(0, start) + fixData
+ fileContents.substring(start + LOAD_FRAME_LEN);
}
return fileContents;
}

private class JavadocOutputStream extends LogOutputStream {
JavadocOutputStream(int level) {
super(Javadoc.this, level);


+ 37
- 0
src/resources/org/apache/tools/ant/taskdefs/javadoc-frame-injections-fix.txt View File

@@ -0,0 +1,37 @@
if (targetPage != "" && !validURL(targetPage))
targetPage = "undefined";
function validURL(url) {
var pos = url.indexOf(".html");
if (pos == -1 || pos != url.length - 5)
return false;
var allowNumber = false;
var allowSep = false;
var seenDot = false;
for (var i = 0; i < url.length - 5; i++) {
var ch = url.charAt(i);
if ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
ch == '$' ||
ch == '_') {
allowNumber = true;
allowSep = true;
} else if ('0' <= ch && ch <= '9'
|| ch == '-') {
if (!allowNumber)
return false;
} else if (ch == '/' || ch == '.') {
if (!allowSep)
return false;
allowNumber = false;
allowSep = false;
if (ch == '.')
seenDot = true;
if (ch == '/' && seenDot)
return false;
} else {
return false;
}
}
return true;
}
function loadFrames() {

Loading…
Cancel
Save