Skip to content
This repository has been archived by the owner on Apr 6, 2021. It is now read-only.
Permalink
Browse files Browse the repository at this point in the history
Fix XSS issues in http_server's dir-listing and error-page.
BUG=
R=sgjesse@google.com

Review URL: https://codereview.chromium.org//225813002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/http_server@34769 260f80e4-7a28-3924-810f-c04153c831b5
  • Loading branch information
ajohnsen@google.com committed Apr 7, 2014
1 parent aa2edbf commit 27c1cbd
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 19 deletions.
34 changes: 23 additions & 11 deletions lib/src/virtual_directory.dart
Expand Up @@ -255,16 +255,17 @@ class VirtualDirectory {
}

response.headers.set(HttpHeaders.LAST_MODIFIED, stats.modified);
var path = request.uri.path;
var path = Uri.decodeComponent(request.uri.path);
var encodedPath = new HtmlEscape().convert(path);
var header =
'''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Index of $path</title>
<title>Index of $encodedPath</title>
</head>
<body>
<h1>Index of $path</h1>
<h1>Index of $encodedPath</h1>
<table>
<tr>
<td>Name</td>
Expand All @@ -284,16 +285,25 @@ $server
response.write(header);

void add(String name, String modified, var size) {
try {
if (size == null) size = "-";
if (modified == null) modified = "";
var p = normalize(join(path, name));
var encodedSize = new HtmlEscape().convert(size.toString());
var encodedModified = new HtmlEscape().convert(modified);
var encodedLink = new HtmlEscape(HtmlEscapeMode.ATTRIBUTE)
.convert(Uri.encodeComponent(normalize(join(path, name))));
var encodedName = new HtmlEscape().convert(name);

var entry =
''' <tr>
<td><a href="$p">$name</a></td>
<td>$modified</td>
<td style="text-align: right">$size</td>
<td><a href="$encodedLink">$encodedName</a></td>
<td>$encodedModified</td>
<td style="text-align: right">$encodedSize</td>
</tr>''';
response.write(entry);
} catch (e) {
print(e);
}
}

if (path != '/') {
Expand Down Expand Up @@ -331,8 +341,10 @@ $server
return;
}
// Default error page.
var path = request.uri.path;
var reason = response.reasonPhrase;
var path = Uri.decodeComponent(request.uri.path);
var encodedPath = new HtmlEscape().convert(path);
var encodedReason = new HtmlEscape().convert(response.reasonPhrase);
var encodedError = new HtmlEscape().convert(error.toString());

var server = response.headers.value(HttpHeaders.SERVER);
if (server == null) server = "";
Expand All @@ -341,10 +353,10 @@ $server
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$reason: $path</title>
<title>$encodedReason: $encodedPath</title>
</head>
<body>
<h1>Error $error at \'$path\': $reason</h1>
<h1>Error $encodedError at \'$encodedPath\': $encodedReason</h1>
$server
</body>
</html>''';
Expand Down
50 changes: 42 additions & 8 deletions test/virtual_directory_test.dart
Expand Up @@ -99,7 +99,7 @@ void main() {

return getAsString(virDir, '/')
.then((result) {
expect(result, contains('Index of /'));
expect(result, contains('Index of &#x2F'));
});
});

Expand All @@ -112,7 +112,7 @@ void main() {

return getAsString(virDir, '/')
.then((result) {
expect(result, contains('Index of /'));
expect(result, contains('Index of &#x2F'));
});
});

Expand All @@ -125,7 +125,41 @@ void main() {

return getAsString(virDir, '/')
.then((result) {
expect(result, contains('Index of /'));
expect(result, contains('Index of &#x2F'));
});
});

testVirtualDir('encoded-dir', (dir) {
var virDir = new VirtualDirectory(dir.path);
new Directory('${dir.path}/alert(\'hacked!\');').createSync();
virDir.allowDirectoryListing = true;

return getAsString(virDir, '/alert(\'hacked!\');')
.then((result) {
expect(result, contains('&#x2F;alert(&#x27;hacked!&#x27;);&#x2F;'));
});
});

testVirtualDir('encoded-path', (dir) {
var virDir = new VirtualDirectory(dir.path);
new Directory('${dir.path}/javascript:alert(document);"').createSync();
virDir.allowDirectoryListing = true;

return getAsString(virDir, '/')
.then((result) {
expect(result, contains('%2Fjavascript%3Aalert(document)%3B%22'));
});
});

testVirtualDir('encoded-special', (dir) {
var virDir = new VirtualDirectory(dir.path);
new Directory('${dir.path}/<>&"').createSync();
virDir.allowDirectoryListing = true;

return getAsString(virDir, '/')
.then((result) {
expect(result, contains('&lt;&gt;&amp;&quot;&#x2F;'));
expect(result, contains('href="%2F%3C%3E%26%22"'));
});
});

Expand All @@ -137,17 +171,17 @@ void main() {

return Future.wait([
getAsString(virDir, '/').then(
(s) => s.contains('recursive/')),
(s) => s.contains('recursive&#x2F;')),
getAsString(virDir, '/').then(
(s) => !s.contains('../')),
getAsString(virDir, '/').then(
(s) => s.contains('Index of /')),
(s) => s.contains('Index of &#x2F;')),
getAsString(virDir, '/recursive').then(
(s) => s.contains('recursive/')),
(s) => s.contains('recursive&#x2F;')),
getAsString(virDir, '/recursive').then(
(s) => s.contains('../')),
(s) => s.contains('..&#x2F;')),
getAsString(virDir, '/recursive').then(
(s) => s.contains('Index of /recursive'))])
(s) => s.contains('Index of &#x2F;recursive'))])
.then((result) {
expect(result, equals([true, true, true, true, true, true]));
});
Expand Down

0 comments on commit 27c1cbd

Please sign in to comment.