diff --git a/ChangeLog b/ChangeLog index bc973a45..4c350157 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2010-10-02 Tatsuhiro Tsujikawa + + Rewritten util::escapePath(). Now it does not replace bad chars:it + performs percent-encode against them. util::fixTaintedBasename() + now replaces "/" with "%2F". Added 0x7f as bad chars in + util::detectDirTraversal(). + * src/util.cc + * test/UtilTest.cc + 2010-10-02 Tatsuhiro Tsujikawa Non-UTF8 filenames are now percent-encoded. For example, filename diff --git a/src/util.cc b/src/util.cc index 1022aa9c..81507870 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1289,7 +1289,8 @@ std::string applyDir(const std::string& dir, const std::string& relPath) std::string fixTaintedBasename(const std::string& src) { - return escapePath(replace(src, A2STR::SLASH_C, A2STR::UNDERSCORE_C)); + static std::string SLASH_REP = "%2F"; + return escapePath(replace(src, A2STR::SLASH_C, SLASH_REP)); } void generateRandomKey(unsigned char* key) @@ -1329,7 +1330,8 @@ bool inPrivateAddress(const std::string& ipv4addr) bool detectDirTraversal(const std::string& s) { for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; ++i) { - if(0x00 <= (*i) && (*i) <= 0x1f) { + unsigned char c = *i; + if(in(c, 0x00, 0x1f) || c == 0x7f) { return true; } } @@ -1345,35 +1347,29 @@ bool detectDirTraversal(const std::string& 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(vbegin(WIN_INVALID_PATH_CHARS), vend(WIN_INVALID_PATH_CHARS), - c) != vend(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('_')); + // We don't escape '/' because we use it as a path separator. +#ifdef __MINGW32__ + static const char WIN_INVALID_PATH_CHARS[] = + { '"', '*', ':', '<', '>', '?', '\\', '|' }; +#endif // __MINGW32__ + std::string d; + for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; ++i) { + unsigned char c = *i; + if(in(c, 0x00, 0x1f) || c == 0x7f +#ifdef __MINGW32__ + || std::find(vbegin(WIN_INVALID_PATH_CHARS), + vend(WIN_INVALID_PATH_CHARS), + c) != vend(WIN_INVALID_PATH_CHARS) + +#endif // __MINGW32__ + ){ + d += StringFormat("%%%02X", c).str(); + } else { + d += *i; + } + } return d; } diff --git a/test/UtilTest.cc b/test/UtilTest.cc index be255fd3..dabc5332 100644 --- a/test/UtilTest.cc +++ b/test/UtilTest.cc @@ -1024,9 +1024,9 @@ void UtilTest::testApplyDir() void UtilTest::testFixTaintedBasename() { - CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a/b")); + CPPUNIT_ASSERT_EQUAL(std::string("a%2Fb"), util::fixTaintedBasename("a/b")); #ifdef __MINGW32__ - CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a\\b")); + CPPUNIT_ASSERT_EQUAL(std::string("a%5Cb"), util::fixTaintedBasename("a\\b")); #else // !__MINGW32__ CPPUNIT_ASSERT_EQUAL(std::string("a\\b"), util::fixTaintedBasename("a\\b")); #endif // !__MINGW32__ @@ -1059,12 +1059,12 @@ void UtilTest::testDetectDirTraversal() void UtilTest::testEscapePath() { - CPPUNIT_ASSERT_EQUAL(std::string("foo_bar__"), + CPPUNIT_ASSERT_EQUAL(std::string("foo%00bar%00%01"), 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")); + CPPUNIT_ASSERT_EQUAL(std::string("foo%5Cbar"), util::escapePath("foo\\bar")); #else // !__MINGW32__ CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), util::escapePath("foo\\bar")); #endif // !__MINGW32__