mirror of https://github.com/aria2/aria2
2010-02-27 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Replaced null or control characters in file path with '_'. For MinGW32 build, additional characters which is not allowed in Windows kernel are also replaced. util::detectDirTraversal() now returns true if given string contains null or control characters. * src/DownloadContext.cc * src/DownloadContext.h * src/Metalink2RequestGroup.cc * src/MetalinkParserController.cc * src/bittorrent_helper.cc * src/util.cc * src/util.h * test/UtilTest.ccpull/1/head
parent
20d78285a8
commit
e8d091af18
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
||||||
|
2010-02-27 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||||
|
|
||||||
|
Replaced null or control characters in file path with '_'. For
|
||||||
|
MinGW32 build, additional characters which is not allowed in
|
||||||
|
Windows kernel are also replaced. util::detectDirTraversal() now
|
||||||
|
returns true if given string contains null or control characters.
|
||||||
|
* src/DownloadContext.cc
|
||||||
|
* src/DownloadContext.h
|
||||||
|
* src/Metalink2RequestGroup.cc
|
||||||
|
* src/MetalinkParserController.cc
|
||||||
|
* src/bittorrent_helper.cc
|
||||||
|
* src/util.cc
|
||||||
|
* src/util.h
|
||||||
|
* test/UtilTest.cc
|
||||||
|
|
||||||
2010-02-27 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
2010-02-27 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||||
|
|
||||||
Discard metalink:file if its name attribute is empty string.
|
Discard metalink:file if its name attribute is empty string.
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
#include "FileEntry.h"
|
#include "FileEntry.h"
|
||||||
#include "StringFormat.h"
|
#include "StringFormat.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -59,7 +60,8 @@ DownloadContext::DownloadContext(size_t pieceLength,
|
||||||
_downloadStartTime(0),
|
_downloadStartTime(0),
|
||||||
_downloadStopTime(_downloadStartTime)
|
_downloadStopTime(_downloadStartTime)
|
||||||
{
|
{
|
||||||
SharedHandle<FileEntry> fileEntry(new FileEntry(path, totalLength, 0));
|
SharedHandle<FileEntry> fileEntry
|
||||||
|
(new FileEntry(util::escapePath(path), totalLength, 0));
|
||||||
_fileEntries.push_back(fileEntry);
|
_fileEntries.push_back(fileEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +110,7 @@ void DownloadContext::setFilePathWithIndex
|
||||||
(size_t index, const std::string& path)
|
(size_t index, const std::string& path)
|
||||||
{
|
{
|
||||||
if(0 < index && index <= _fileEntries.size()) {
|
if(0 < index && index <= _fileEntries.size()) {
|
||||||
|
// We don't escape path because path may come from users.
|
||||||
_fileEntries[index-1]->setPath(path);
|
_fileEntries[index-1]->setPath(path);
|
||||||
} else {
|
} else {
|
||||||
throw DL_ABORT_EX(StringFormat("No such file with index=%u",
|
throw DL_ABORT_EX(StringFormat("No such file with index=%u",
|
||||||
|
|
|
@ -216,10 +216,11 @@ public:
|
||||||
|
|
||||||
void setFileFilter(IntSequence seq);
|
void setFileFilter(IntSequence seq);
|
||||||
|
|
||||||
// Sets file path for specified index. index starts from 1. The index
|
// Sets file path for specified index. index starts from 1. The
|
||||||
// is the same used in setFileFilter(). Please note that path is
|
// index is the same used in setFileFilter(). Please note that path
|
||||||
// not the actual file path. The actual file path is
|
// is not the actual file path. The actual file path is
|
||||||
// getDir()+"/"+path.
|
// getDir()+"/"+path. path is not escaped by util::escapePath() in
|
||||||
|
// this function.
|
||||||
void setFilePathWithIndex(size_t index, const std::string& path);
|
void setFilePathWithIndex(size_t index, const std::string& path);
|
||||||
|
|
||||||
void setAttribute(const std::string& key, const BDE& value);
|
void setAttribute(const std::string& key, const BDE& value);
|
||||||
|
|
|
@ -287,7 +287,8 @@ Metalink2RequestGroup::createRequestGroup
|
||||||
AccumulateNonP2PUrl(uris));
|
AccumulateNonP2PUrl(uris));
|
||||||
SharedHandle<FileEntry> fe
|
SharedHandle<FileEntry> fe
|
||||||
(new FileEntry
|
(new FileEntry
|
||||||
(util::applyDir(option->get(PREF_DIR), (*i)->file->getPath()),
|
(util::applyDir(option->get(PREF_DIR),
|
||||||
|
util::escapePath((*i)->file->getPath())),
|
||||||
(*i)->file->getLength(), offset, uris));
|
(*i)->file->getLength(), offset, uris));
|
||||||
if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) {
|
if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) {
|
||||||
fe->disableSingleHostMultiConnection();
|
fe->disableSingleHostMultiConnection();
|
||||||
|
|
|
@ -86,7 +86,7 @@ void MetalinkParserController::setFileNameOfEntry(const std::string& filename)
|
||||||
if(_tEntry->file.isNull()) {
|
if(_tEntry->file.isNull()) {
|
||||||
_tEntry->file.reset(new FileEntry(path, 0, 0));
|
_tEntry->file.reset(new FileEntry(path, 0, 0));
|
||||||
} else {
|
} else {
|
||||||
_tEntry->file->setPath(path);
|
_tEntry->file->setPath(util::escapePath(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,7 @@ static void extractFileEntries
|
||||||
std::deque<std::string> uris;
|
std::deque<std::string> uris;
|
||||||
createUri(urlList.begin(), urlList.end(), std::back_inserter(uris), path);
|
createUri(urlList.begin(), urlList.end(), std::back_inserter(uris), path);
|
||||||
SharedHandle<FileEntry> fileEntry
|
SharedHandle<FileEntry> fileEntry
|
||||||
(new FileEntry(util::applyDir(ctx->getDir(), path),
|
(new FileEntry(util::applyDir(ctx->getDir(), util::escapePath(path)),
|
||||||
fileLengthData.i(),
|
fileLengthData.i(),
|
||||||
offset, uris));
|
offset, uris));
|
||||||
fileEntry->setOriginalName(path);
|
fileEntry->setOriginalName(path);
|
||||||
|
@ -287,7 +287,8 @@ static void extractFileEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedHandle<FileEntry> fileEntry
|
SharedHandle<FileEntry> fileEntry
|
||||||
(new FileEntry(util::applyDir(ctx->getDir(), name),totalLength, 0,
|
(new FileEntry(util::applyDir(ctx->getDir(), util::escapePath(name)),
|
||||||
|
totalLength, 0,
|
||||||
uris));
|
uris));
|
||||||
fileEntry->setOriginalName(name);
|
fileEntry->setOriginalName(name);
|
||||||
fileEntries.push_back(fileEntry);
|
fileEntries.push_back(fileEntry);
|
||||||
|
|
42
src/util.cc
42
src/util.cc
|
@ -1129,8 +1129,7 @@ std::string applyDir(const std::string& dir, const std::string& relPath)
|
||||||
|
|
||||||
std::string fixTaintedBasename(const std::string& src)
|
std::string fixTaintedBasename(const std::string& src)
|
||||||
{
|
{
|
||||||
return replace(replace(src, A2STR::SLASH_C, A2STR::UNDERSCORE_C),
|
return escapePath(replace(src, A2STR::SLASH_C, A2STR::UNDERSCORE_C));
|
||||||
A2STR::BACK_SLASH_C, A2STR::UNDERSCORE_C);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateRandomKey(unsigned char* key)
|
void generateRandomKey(unsigned char* key)
|
||||||
|
@ -1169,6 +1168,11 @@ bool inPrivateAddress(const std::string& ipv4addr)
|
||||||
|
|
||||||
bool detectDirTraversal(const std::string& s)
|
bool detectDirTraversal(const std::string& s)
|
||||||
{
|
{
|
||||||
|
for(std::string::const_iterator i = s.begin(); i != s.end(); ++i) {
|
||||||
|
if(0x00 <= (*i) && (*i) <= 0x1f) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return s == A2STR::DOT_C ||
|
return s == A2STR::DOT_C ||
|
||||||
s == ".." ||
|
s == ".." ||
|
||||||
util::startsWith(s, A2STR::SLASH_C) ||
|
util::startsWith(s, A2STR::SLASH_C) ||
|
||||||
|
@ -1181,6 +1185,40 @@ bool detectDirTraversal(const std::string& s)
|
||||||
util::endsWith(s, "/..");
|
util::endsWith(s, "/..");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class EscapePath {
|
||||||
|
private:
|
||||||
|
char _repChar;
|
||||||
|
public:
|
||||||
|
EscapePath(const char& repChar):_repChar(repChar) {}
|
||||||
|
|
||||||
|
char operator()(const char& c) {
|
||||||
|
if(0x00 <= c && c <=0x1f) {
|
||||||
|
return _repChar;
|
||||||
|
}
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
// We don't escape '/' because we use it as a path separator.
|
||||||
|
static const char WIN_INVALID_PATH_CHARS[] =
|
||||||
|
{ '"', '*', ':', '<', '>', '?', '\\', '|' };
|
||||||
|
if(std::find(&WIN_INVALID_PATH_CHARS[0],
|
||||||
|
&WIN_INVALID_PATH_CHARS[arrayLength(WIN_INVALID_PATH_CHARS)],
|
||||||
|
c) !=
|
||||||
|
&WIN_INVALID_PATH_CHARS[arrayLength(WIN_INVALID_PATH_CHARS)]) {
|
||||||
|
return _repChar;
|
||||||
|
}
|
||||||
|
#endif // __MINGW32__
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string escapePath(const std::string& s)
|
||||||
|
{
|
||||||
|
std::string d = s;
|
||||||
|
std::transform(d.begin(), d.end(), d.begin(), EscapePath('_'));
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
11
src/util.h
11
src/util.h
|
@ -371,7 +371,8 @@ std::string applyDir(const std::string& dir, const std::string& relPath);
|
||||||
// basename contains dir component. This should be avoided. This
|
// basename contains dir component. This should be avoided. This
|
||||||
// function is created to fix these issues. This function expects src
|
// function is created to fix these issues. This function expects src
|
||||||
// should be non-percent-encoded basename. Currently, this function
|
// should be non-percent-encoded basename. Currently, this function
|
||||||
// replaces '/' and '\' with '_'.
|
// replaces '/' with '_' and result string is passed to escapePath()
|
||||||
|
// function and its result is returned.
|
||||||
std::string fixTaintedBasename(const std::string& src);
|
std::string fixTaintedBasename(const std::string& src);
|
||||||
|
|
||||||
// Generates 20 bytes random key and store it to the address pointed
|
// Generates 20 bytes random key and store it to the address pointed
|
||||||
|
@ -382,9 +383,15 @@ void generateRandomKey(unsigned char* key);
|
||||||
bool inPrivateAddress(const std::string& ipv4addr);
|
bool inPrivateAddress(const std::string& ipv4addr);
|
||||||
|
|
||||||
// Returns true if s contains directory traversal path component such
|
// Returns true if s contains directory traversal path component such
|
||||||
// as '..'.
|
// as '..' or it contains null or control character which may fool
|
||||||
|
// user.
|
||||||
bool detectDirTraversal(const std::string& s);
|
bool detectDirTraversal(const std::string& s);
|
||||||
|
|
||||||
|
// Replaces null(0x00) and control character(0x01-0x1f) with '_'. If
|
||||||
|
// __MINGW32__ is defined, following characters are also replaced with
|
||||||
|
// '_': '"', '*', ':', '<', '>', '?', '\', '|'.
|
||||||
|
std::string escapePath(const std::string& s);
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -62,6 +62,7 @@ class UtilTest:public CppUnit::TestFixture {
|
||||||
CPPUNIT_TEST(testFixTaintedBasename);
|
CPPUNIT_TEST(testFixTaintedBasename);
|
||||||
CPPUNIT_TEST(testIsNumericHost);
|
CPPUNIT_TEST(testIsNumericHost);
|
||||||
CPPUNIT_TEST(testDetectDirTraversal);
|
CPPUNIT_TEST(testDetectDirTraversal);
|
||||||
|
CPPUNIT_TEST(testEscapePath);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -112,6 +113,7 @@ public:
|
||||||
void testFixTaintedBasename();
|
void testFixTaintedBasename();
|
||||||
void testIsNumericHost();
|
void testIsNumericHost();
|
||||||
void testDetectDirTraversal();
|
void testDetectDirTraversal();
|
||||||
|
void testEscapePath();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1006,8 +1008,11 @@ void UtilTest::testApplyDir()
|
||||||
void UtilTest::testFixTaintedBasename()
|
void UtilTest::testFixTaintedBasename()
|
||||||
{
|
{
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a/b"));
|
CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a/b"));
|
||||||
|
#ifdef __MINGW32__
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a\\b"));
|
CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a\\b"));
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string("a__b"), util::fixTaintedBasename("a\\/b"));
|
#else // !__MINGW32__
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("a\\b"), util::fixTaintedBasename("a\\b"));
|
||||||
|
#endif // !__MINGW32__
|
||||||
}
|
}
|
||||||
|
|
||||||
void UtilTest::testIsNumericHost()
|
void UtilTest::testIsNumericHost()
|
||||||
|
@ -1030,8 +1035,22 @@ void UtilTest::testDetectDirTraversal()
|
||||||
CPPUNIT_ASSERT(util::detectDirTraversal(".."));
|
CPPUNIT_ASSERT(util::detectDirTraversal(".."));
|
||||||
CPPUNIT_ASSERT(util::detectDirTraversal("/"));
|
CPPUNIT_ASSERT(util::detectDirTraversal("/"));
|
||||||
CPPUNIT_ASSERT(util::detectDirTraversal("foo/"));
|
CPPUNIT_ASSERT(util::detectDirTraversal("foo/"));
|
||||||
|
CPPUNIT_ASSERT(util::detectDirTraversal("\t"));
|
||||||
CPPUNIT_ASSERT(!util::detectDirTraversal("foo/bar"));
|
CPPUNIT_ASSERT(!util::detectDirTraversal("foo/bar"));
|
||||||
CPPUNIT_ASSERT(!util::detectDirTraversal("foo"));
|
CPPUNIT_ASSERT(!util::detectDirTraversal("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UtilTest::testEscapePath()
|
||||||
|
{
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("foo_bar__"),
|
||||||
|
util::escapePath(std::string("foo")+(char)0x00+
|
||||||
|
std::string("bar")+(char)0x00+
|
||||||
|
(char)0x01));
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("foo_bar"), util::escapePath("foo\\bar"));
|
||||||
|
#else // !__MINGW32__
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), util::escapePath("foo\\bar"));
|
||||||
|
#endif // !__MINGW32__
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
Loading…
Reference in New Issue