1313#include < fstream>
1414#include < sstream>
1515#include < algorithm>
16+ #include < array>
1617#include < functional>
1718#include < set>
1819#include < boost/algorithm/string.hpp>
@@ -335,6 +336,7 @@ namespace torrents
335336 return len;
336337 }
337338
339+
338340 size_t Torrent::ParseInfo (std::string_view buf)
339341 {
340342 size_t len = ParseDictionary (buf, [this ](std::string_view key, std::string_view buf)->size_t
@@ -348,7 +350,15 @@ namespace torrents
348350 else if (key == " name" )
349351 {
350352 auto [name, l] = ExtractByteString (buf);
351- if (l) m_Name = name;
353+ if (l)
354+ {
355+ if (!IsSafeName (name))
356+ {
357+ LogPrint (eLogError, " Torrents: Unsafe name in torrent: " , name);
358+ return 0 ;
359+ }
360+ m_Name = name;
361+ }
352362 return l;
353363 }
354364 else if (key == " piece length" )
@@ -373,6 +383,29 @@ namespace torrents
373383 return len;
374384 }
375385
386+ bool Torrent::IsSafeName (std::string_view name)
387+ {
388+ // names come from a torrent file, that is from a stranger. A component
389+ // that is empty, a dot pair, absolute or separated would take the path
390+ // out of the torrents folder, and some names are refused by Windows
391+ if (name.empty () || name == " ." || name == " .." ) return false ;
392+ if (name.back () == ' .' || name.back () == ' ' ) return false ; // Windows drops those
393+ for (char ch: name)
394+ if (ch == ' /' || ch == ' \\ ' || ch == ' :' || ch == ' <' || ch == ' >' ||
395+ ch == ' "' || ch == ' |' || ch == ' ?' || ch == ' *' || (unsigned char )ch < 0x20 )
396+ return false ;
397+ using namespace std ::string_view_literals;
398+ static constexpr std::array reserved
399+ {
400+ " CON" sv, " PRN" sv, " AUX" sv, " NUL" sv,
401+ " COM1" sv, " COM2" sv, " COM3" sv, " COM4" sv, " COM5" sv, " COM6" sv, " COM7" sv, " COM8" sv, " COM9" sv,
402+ " LPT1" sv, " LPT2" sv, " LPT3" sv, " LPT4" sv, " LPT5" sv, " LPT6" sv, " LPT7" sv, " LPT8" sv, " LPT9" sv
403+ };
404+ std::string stem (name.substr (0 , name.find (' .' )));
405+ boost::to_upper (stem);
406+ return std::find (reserved.begin (), reserved.end (), stem) == reserved.end ();
407+ }
408+
376409 size_t Torrent::ParseFiles (std::string_view buf)
377410 {
378411 m_Length = 0 ;
@@ -386,7 +419,14 @@ namespace torrents
386419 auto [subdirs, l] = ParseStringList (value);
387420 if (l)
388421 for (const auto & it: subdirs)
422+ {
423+ if (!IsSafeName (it))
424+ {
425+ LogPrint (eLogError, " Torrents: Unsafe path component in torrent: " , it);
426+ return 0 ;
427+ }
389428 filePath /= it;
429+ }
390430 return l;
391431 }
392432 else if (key == " length" )
0 commit comments