Skip to content

Commit

Permalink
First step towards validating filenames
Browse files Browse the repository at this point in the history
Signed-off-by: Geoff Hutchison <[email protected]>
  • Loading branch information
ghutchis committed Oct 10, 2023
1 parent a0f41dc commit effd7c3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
43 changes: 38 additions & 5 deletions avogadro/io/fileformat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "fileformat.h"

#include <algorithm>
#include <fstream>
#include <locale>
#include <sstream>
Expand All @@ -15,22 +16,54 @@ using std::ifstream;
using std::locale;
using std::ofstream;

FileFormat::FileFormat() : m_mode(None), m_in(nullptr), m_out(nullptr)
{
}
FileFormat::FileFormat() : m_mode(None), m_in(nullptr), m_out(nullptr) {}

FileFormat::~FileFormat()
{
delete m_in;
delete m_out;
}

bool FileFormat::validateFileName(const std::string& fileName)
{
bool valid = !fileName.empty();

if (valid) {
// check if the filename contains invalid characters
static std::string forbiddenChars(",^@={}[]~!?:&*\"|#%<>$\"'();`'");
valid = fileName.find_first_of(forbiddenChars) == std::string::npos;

// check if the filename contains ".." which we should not allow
valid = valid && fileName.find("..") == std::string::npos;
}

// Finally check against Windows names
// .. we do this on all platforms because CON.cif, for example
// is problematic to send to a Windows user.
if (valid) {
static std::string forbiddenNames(
"CON PRN AUX NUL COM1 COM2 COM3 COM4 COM5 "
"COM6 COM7 COM8 COM9 LPT1 LPT2 LPT3 LPT4 "
"LPT5 LPT6 LPT7 LPT8 LPT9");
// case insensitive search, since con.txt is also a problem
// https://stackoverflow.com/a/19839371/131896
auto it = std::search(fileName.begin(), fileName.end(),
forbiddenNames.begin(), forbiddenNames.end(),
[](unsigned char ch1, unsigned char ch2) {
return std::toupper(ch1) == std::toupper(ch2);
});
valid = (it == fileName.end());
}

return valid;
}

bool FileFormat::open(const std::string& fileName_, Operation mode_)
{
close();
m_fileName = fileName_;
m_mode = mode_;
if (!m_fileName.empty()) {
if (!m_fileName.empty() && validateFileName(m_fileName)) {
// Imbue the standard C locale.
locale cLocale("C");
if (m_mode & Read) {
Expand Down Expand Up @@ -143,4 +176,4 @@ void FileFormat::appendError(const std::string& errorString, bool newLine)
m_error += "\n";
}

} // namespace Avogadro
} // namespace Avogadro::Io
13 changes: 13 additions & 0 deletions avogadro/io/fileformat.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ class AVOGADROIO_EXPORT FileFormat
*/
virtual Operations supportedOperations() const = 0;


/**
* @brief Validates the given file name.
*
* Checks if the filename contains any invalid characters (e.g. ..)
* Also checks if the filename contains a restricted name on Windows.
* e.g., CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, etc.
*
* @param fileName The name of the file to be validated.
* @return true if the file name is valid, false otherwise.
*/
static bool validateFileName(const std::string& fileName);

/**
* @brief Open the specified file in Read or Write mode.
* @return True on success, false on failure.
Expand Down

0 comments on commit effd7c3

Please sign in to comment.