Bug 1412081 - Add ability to blacklist file paths on Unix platforms r=mayhemer

--HG--
extra : rebase_source : 6894f5c3df745519e5e9db5b7bf6f004922152d1
This commit is contained in:
Valentin Gosu 2018-06-20 02:52:12 +02:00
Родитель 29e57a2096
Коммит 5bd326c5f5
5 изменённых файлов: 453 добавлений и 96 удалений

Просмотреть файл

@ -6,25 +6,46 @@
#include "FilePreferences.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Tokenizer.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsString.h"
namespace mozilla {
namespace FilePreferences {
static bool sBlockUNCPaths = false;
typedef nsTArray<nsString> Paths;
typedef nsTArray<nsString> WinPaths;
static Paths& PathArray()
static WinPaths& PathWhitelist()
{
static Paths sPaths;
static WinPaths sPaths;
return sPaths;
}
static void AllowDirectory(char const* directory)
#ifdef XP_WIN
typedef char16_t char_path_t;
#else
typedef char char_path_t;
#endif
typedef nsTArray<nsTString<char_path_t>> Paths;
static StaticAutoPtr<Paths> sBlacklist;
static Paths& PathBlacklist()
{
if (!sBlacklist) {
sBlacklist = new nsTArray<nsTString<char_path_t>>();
ClearOnShutdown(&sBlacklist);
}
return *sBlacklist;
}
static void AllowUNCDirectory(char const* directory)
{
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(directory, getter_AddRefs(file));
@ -44,135 +65,160 @@ static void AllowDirectory(char const* directory)
return;
}
if (!PathArray().Contains(path)) {
PathArray().AppendElement(path);
if (!PathWhitelist().Contains(path)) {
PathWhitelist().AppendElement(path);
}
}
void InitPrefs()
{
sBlockUNCPaths = Preferences::GetBool("network.file.disable_unc_paths", false);
PathBlacklist().Clear();
nsTAutoString<char_path_t> blacklist;
#ifdef XP_WIN
Preferences::GetString("network.file.path_blacklist", blacklist);
#else
Preferences::GetCString("network.file.path_blacklist", blacklist);
#endif
TTokenizer<char_path_t> p(blacklist);
while (!p.CheckEOF()) {
nsTString<char_path_t> path;
Unused << p.ReadUntil(TTokenizer<char_path_t>::Token::Char(','), path);
path.Trim(" ");
if (!path.IsEmpty()) {
PathBlacklist().AppendElement(path);
}
Unused << p.CheckChar(',');
}
}
void InitDirectoriesWhitelist()
{
// NS_GRE_DIR is the installation path where the binary resides.
AllowDirectory(NS_GRE_DIR);
AllowUNCDirectory(NS_GRE_DIR);
// NS_APP_USER_PROFILE_50_DIR and NS_APP_USER_PROFILE_LOCAL_50_DIR are the two
// parts of the profile we store permanent and local-specific data.
AllowDirectory(NS_APP_USER_PROFILE_50_DIR);
AllowDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
AllowUNCDirectory(NS_APP_USER_PROFILE_50_DIR);
AllowUNCDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
}
namespace { // anon
class Normalizer : public Tokenizer16
template <typename TChar>
class TNormalizer
: public TTokenizer<TChar>
{
typedef TTokenizer<TChar> base;
public:
Normalizer(const nsAString& aFilePath, const Token& aSeparator);
bool Get(nsAString& aNormalizedFilePath);
typedef typename base::Token Token;
TNormalizer(const nsTSubstring<TChar>& aFilePath, const Token& aSeparator)
: TTokenizer<TChar>(aFilePath)
, mSeparator(aSeparator)
{
}
bool Get(nsTSubstring<TChar>& aNormalizedFilePath)
{
aNormalizedFilePath.Truncate();
// Windows UNC paths begin with double separator (\\)
// Linux paths begin with just one separator (/)
// If we want to use the normalizer for regular windows paths this code
// will need to be updated.
#ifdef XP_WIN
if (base::Check(mSeparator)) {
aNormalizedFilePath.Append(mSeparator.AsChar());
}
#endif
if (base::Check(mSeparator)) {
aNormalizedFilePath.Append(mSeparator.AsChar());
}
while (base::HasInput()) {
if (!ConsumeName()) {
return false;
}
}
for (auto const& name : mStack) {
aNormalizedFilePath.Append(name);
}
return true;
}
private:
bool ConsumeName();
bool CheckParentDir();
bool CheckCurrentDir();
bool CheckSeparator();
Token const mSeparator;
nsTArray<nsDependentSubstring> mStack;
};
Normalizer::Normalizer(const nsAString& aFilePath, const Token& aSeparator)
: Tokenizer16(aFilePath)
, mSeparator(aSeparator)
{
}
bool Normalizer::Get(nsAString& aNormalizedFilePath)
{
aNormalizedFilePath.Truncate();
if (Check(mSeparator)) {
aNormalizedFilePath.Append(mSeparator.AsChar());
}
if (Check(mSeparator)) {
aNormalizedFilePath.Append(mSeparator.AsChar());
}
while (HasInput()) {
if (!ConsumeName()) {
return false;
}
}
for (auto const& name : mStack) {
aNormalizedFilePath.Append(name);
}
return true;
}
bool Normalizer::ConsumeName()
{
if (CheckEOF()) {
return true;
}
if (CheckCurrentDir()) {
return true;
}
if (CheckParentDir()) {
if (!mStack.Length()) {
// This means there are more \.. than valid names
return false;
bool ConsumeName()
{
if (base::CheckEOF()) {
return true;
}
mStack.RemoveLastElement();
if (CheckCurrentDir()) {
return true;
}
if (CheckParentDir()) {
if (!mStack.Length()) {
// This means there are more \.. than valid names
return false;
}
mStack.RemoveLastElement();
return true;
}
nsTDependentSubstring<TChar> name;
if (base::ReadUntil(mSeparator, name, base::INCLUDE_LAST) && name.Length() == 1) {
// this means and empty name (a lone slash), which is illegal
return false;
}
mStack.AppendElement(name);
return true;
}
nsDependentSubstring name;
if (ReadUntil(mSeparator, name, INCLUDE_LAST) && name.Length() == 1) {
// this means and empty name (a lone slash), which is illegal
bool CheckParentDir()
{
typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
if (base::CheckChar('.') && base::CheckChar('.') && CheckSeparator()) {
return true;
}
base::mCursor = cursor;
return false;
}
mStack.AppendElement(name);
return true;
}
bool CheckCurrentDir()
{
typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
if (base::CheckChar('.') && CheckSeparator()) {
return true;
}
bool Normalizer::CheckCurrentDir()
{
nsString::const_char_iterator cursor = mCursor;
if (CheckChar('.') && CheckSeparator()) {
return true;
base::mCursor = cursor;
return false;
}
mCursor = cursor;
return false;
}
bool Normalizer::CheckParentDir()
{
nsString::const_char_iterator cursor = mCursor;
if (CheckChar('.') && CheckChar('.') && CheckSeparator()) {
return true;
bool CheckSeparator()
{
return base::Check(mSeparator) || base::CheckEOF();
}
mCursor = cursor;
return false;
}
bool Normalizer::CheckSeparator()
{
return Check(mSeparator) || CheckEOF();
}
Token const mSeparator;
nsTArray<nsTDependentSubstring<TChar>> mStack;
};
} // anon
bool IsBlockedUNCPath(const nsAString& aFilePath)
{
typedef TNormalizer<char16_t> Normalizer;
if (!sBlockUNCPaths) {
return false;
}
@ -187,7 +233,7 @@ bool IsBlockedUNCPath(const nsAString& aFilePath)
return true;
}
for (const auto& allowedPrefix : PathArray()) {
for (const auto& allowedPrefix : PathWhitelist()) {
if (StringBeginsWith(normalized, allowedPrefix)) {
if (normalized.Length() == allowedPrefix.Length()) {
return false;
@ -207,6 +253,44 @@ bool IsBlockedUNCPath(const nsAString& aFilePath)
return true;
}
#ifdef XP_WIN
const char kPathSeparator = '\\';
#else
const char kPathSeparator = '/';
#endif
bool IsAllowedPath(const nsTSubstring<char_path_t>& aFilePath)
{
typedef TNormalizer<char_path_t> Normalizer;
// If sBlacklist has been cleared at shutdown, we must avoid calling
// PathBlacklist() again, as that will recreate the array and we will leak.
if (!sBlacklist) {
return true;
}
if (PathBlacklist().Length() == 0) {
return true;
}
nsTAutoString<char_path_t> normalized;
if (!Normalizer(aFilePath, Normalizer::Token::Char(kPathSeparator)).Get(normalized)) {
// Broken paths are considered invalid and thus inaccessible
return false;
}
for (const auto& prefix : PathBlacklist()) {
if (StringBeginsWith(normalized, prefix)) {
if (normalized.Length() > prefix.Length() &&
normalized[prefix.Length()] != kPathSeparator) {
continue;
}
return false;
}
}
return true;
}
void testing::SetBlockUNCPaths(bool aBlock)
{
sBlockUNCPaths = aBlock;
@ -214,11 +298,12 @@ void testing::SetBlockUNCPaths(bool aBlock)
void testing::AddDirectoryToWhitelist(nsAString const & aPath)
{
PathArray().AppendElement(aPath);
PathWhitelist().AppendElement(aPath);
}
bool testing::NormalizePath(nsAString const & aPath, nsAString & aNormalized)
{
typedef TNormalizer<char16_t> Normalizer;
Normalizer normalizer(aPath, Normalizer::Token::Char('\\'));
return normalizer.Get(aNormalized);
}

Просмотреть файл

@ -13,6 +13,12 @@ void InitPrefs();
void InitDirectoriesWhitelist();
bool IsBlockedUNCPath(const nsAString& aFilePath);
#ifdef XP_WIN
bool IsAllowedPath(const nsAString& aFilePath);
#else
bool IsAllowedPath(const nsACString& aFilePath);
#endif
namespace testing {
void SetBlockUNCPaths(bool aBlock);

Просмотреть файл

@ -12,6 +12,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Sprintf.h"
#include "mozilla/FilePreferences.h"
#include <sys/types.h>
#include <sys/stat.h>
@ -84,6 +85,8 @@ using namespace mozilla;
do { \
if (mPath.IsEmpty()) \
return NS_ERROR_NOT_INITIALIZED; \
if (!FilePreferences::IsAllowedPath(mPath)) \
return NS_ERROR_FILE_ACCESS_DENIED; \
} while(0)
/* directory enumerator */
@ -139,6 +142,13 @@ nsDirEnumeratorUnix::Init(nsLocalFile* aParent,
return NS_ERROR_FILE_INVALID_PATH;
}
// When enumerating the directory, the paths must have a slash at the end.
nsAutoCString dirPathWithSlash(dirPath);
dirPathWithSlash.Append('/');
if (!FilePreferences::IsAllowedPath(dirPathWithSlash)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
if (NS_FAILED(aParent->GetNativePath(mParentPath))) {
return NS_ERROR_FAILURE;
}
@ -270,6 +280,11 @@ nsLocalFile::nsLocalFileConstructor(nsISupports* aOuter,
bool
nsLocalFile::FillStatCache()
{
if (!FilePreferences::IsAllowedPath(mPath)) {
errno = EACCES;
return false;
}
if (STAT(mPath.get(), &mCachedStat) == -1) {
// try lstat it may be a symlink
if (LSTAT(mPath.get(), &mCachedStat) == -1) {
@ -312,6 +327,11 @@ nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
mPath = aFilePath;
}
if (!FilePreferences::IsAllowedPath(mPath)) {
mPath.Truncate();
return NS_ERROR_FILE_ACCESS_DENIED;
}
// trim off trailing slashes
ssize_t len = mPath.Length();
while ((len > 1) && (mPath[len - 1] == '/')) {
@ -325,6 +345,10 @@ nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
NS_IMETHODIMP
nsLocalFile::CreateAllAncestors(uint32_t aPermissions)
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
// <jband> I promise to play nice
char* buffer = mPath.BeginWriting();
char* slashp = buffer;
@ -396,6 +420,9 @@ NS_IMETHODIMP
nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
PRFileDesc** aResult)
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
*aResult = PR_Open(mPath.get(), aFlags, aMode);
if (!*aResult) {
return NS_ErrorAccordingToNSPR();
@ -417,6 +444,9 @@ nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
NS_IMETHODIMP
nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult)
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
*aResult = fopen(mPath.get(), aMode);
if (!*aResult) {
return NS_ERROR_FAILURE;
@ -443,6 +473,10 @@ nsresult
nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
uint32_t aPermissions, PRFileDesc** aResult)
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) {
return NS_ERROR_FILE_UNKNOWN_TYPE;
}
@ -492,6 +526,10 @@ nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
NS_IMETHODIMP
nsLocalFile::Create(uint32_t aType, uint32_t aPermissions)
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
PRFileDesc* junk = nullptr;
nsresult rv = CreateAndKeepOpen(aType,
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
@ -547,6 +585,10 @@ nsLocalFile::Normalize()
char resolved_path[PATH_MAX] = "";
char* resolved_path_ptr = nullptr;
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
resolved_path_ptr = realpath(mPath.get(), resolved_path);
// if there is an error, the return is null.
@ -1011,6 +1053,10 @@ nsLocalFile::MoveToNative(nsIFile* aNewParent, const nsACString& aNewName)
return rv;
}
if (!FilePreferences::IsAllowedPath(newPathName)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
// try for atomic rename, falling back to copy/delete
if (rename(mPath.get(), newPathName.get()) < 0) {
if (errno == EXDEV) {
@ -1953,6 +1999,10 @@ nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
NS_IMETHODIMP
nsLocalFile::Reveal()
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
#ifdef MOZ_WIDGET_GTK
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
if (!giovfs) {
@ -1996,6 +2046,10 @@ nsLocalFile::Reveal()
NS_IMETHODIMP
nsLocalFile::Launch()
{
if (!FilePreferences::IsAllowedPath(mPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
#ifdef MOZ_WIDGET_GTK
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
if (!giovfs) {
@ -2150,6 +2204,10 @@ nsLocalFile::RenameToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
return rv;
}
if (!FilePreferences::IsAllowedPath(newPathName)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
// try for atomic rename
if (rename(mPath.get(), newPathName.get()) < 0) {
if (errno == EXDEV) {

Просмотреть файл

@ -0,0 +1,203 @@
#include "gtest/gtest.h"
#include "mozilla/FilePreferences.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScopeExit.h"
#include "nsISimpleEnumerator.h"
using namespace mozilla;
TEST(TestFilePreferencesUnix, Parsing)
{
#define kBlacklisted "/tmp/blacklisted"
#define kBlacklistedDir "/tmp/blacklisted/"
#define kBlacklistedFile "/tmp/blacklisted/file"
#define kOther "/tmp/other"
#define kOtherDir "/tmp/other/"
#define kOtherFile "/tmp/other/file"
#define kAllowed "/tmp/allowed"
// This is run on exit of this function to make sure we clear the pref
// and that behaviour with the pref cleared is correct.
auto cleanup = MakeScopeExit([&] {
nsresult rv = Preferences::ClearUser("network.file.path_blacklist");
ASSERT_EQ(rv, NS_OK);
FilePreferences::InitPrefs();
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklisted)), true);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedDir)), true);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedFile)), true);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kAllowed)), true);
});
auto CheckPrefs = [](const nsACString& aPaths)
{
nsresult rv;
rv = Preferences::SetCString("network.file.path_blacklist", aPaths);
ASSERT_EQ(rv, NS_OK);
FilePreferences::InitPrefs();
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedDir)), false);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedDir)), false);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedFile)), false);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklisted)), false);
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kAllowed)), true);
};
CheckPrefs(NS_LITERAL_CSTRING(kBlacklisted));
CheckPrefs(NS_LITERAL_CSTRING(kBlacklisted "," kOther));
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kOtherFile)), false);
CheckPrefs(NS_LITERAL_CSTRING(kBlacklisted "," kOther ","));
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kOtherFile)), false);
}
TEST(TestFilePreferencesUnix, Simple)
{
nsAutoCString tempPath;
// This is the directory we will blacklist
nsCOMPtr<nsIFile> blacklistedDir;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(blacklistedDir));
ASSERT_EQ(rv, NS_OK);
rv = blacklistedDir->GetNativePath(tempPath);
ASSERT_EQ(rv, NS_OK);
rv = blacklistedDir->AppendNative(NS_LITERAL_CSTRING("blacklisted_dir"));
ASSERT_EQ(rv, NS_OK);
// This is executed at exit to clean up after ourselves.
auto cleanup = MakeScopeExit([&] {
nsresult rv = Preferences::ClearUser("network.file.path_blacklist");
ASSERT_EQ(rv, NS_OK);
FilePreferences::InitPrefs();
rv = blacklistedDir->Remove(true);
ASSERT_EQ(rv, NS_OK);
});
// Create the directory
rv = blacklistedDir->Create(nsIFile::DIRECTORY_TYPE, 0666);
ASSERT_EQ(rv, NS_OK);
// This is the file we will try to access
nsCOMPtr<nsIFile> blacklistedFile;
rv = blacklistedDir->Clone(getter_AddRefs(blacklistedFile));
ASSERT_EQ(rv, NS_OK);
rv = blacklistedFile->AppendNative(NS_LITERAL_CSTRING("test_file"));
// Create the file
ASSERT_EQ(rv, NS_OK);
rv = blacklistedFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
// Get the path for the blacklist
nsAutoCString blackListPath;
rv = blacklistedDir->GetNativePath(blackListPath);
ASSERT_EQ(rv, NS_OK);
// Set the pref and make sure it is enforced
rv = Preferences::SetCString("network.file.path_blacklist", blackListPath);
ASSERT_EQ(rv, NS_OK);
FilePreferences::InitPrefs();
// Check that we can't access some of the file attributes
int64_t size;
rv = blacklistedFile->GetFileSize(&size);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
bool exists;
rv = blacklistedFile->Exists(&exists);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
// Check that we can't enumerate the directory
nsCOMPtr<nsISimpleEnumerator> dirEnumerator;
rv = blacklistedDir->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
nsCOMPtr<nsIFile> newPath;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendNative(NS_LITERAL_CSTRING("."));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendNative(NS_LITERAL_CSTRING("blacklisted_dir"));
ASSERT_EQ(rv, NS_OK);
rv = newPath->Exists(&exists);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
rv = newPath->AppendNative(NS_LITERAL_CSTRING("test_file"));
ASSERT_EQ(rv, NS_OK);
rv = newPath->Exists(&exists);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
// Check that ./ does not bypass the filter
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendRelativeNativePath(NS_LITERAL_CSTRING("./blacklisted_dir/file"));
ASSERT_EQ(rv, NS_OK);
rv = newPath->Exists(&exists);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
// Check that .. does not bypass the filter
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendRelativeNativePath(NS_LITERAL_CSTRING("allowed/../blacklisted_dir/file"));
ASSERT_EQ(rv, NS_OK);
rv = newPath->Exists(&exists);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendNative(NS_LITERAL_CSTRING("allowed"));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendNative(NS_LITERAL_CSTRING(".."));
ASSERT_EQ(rv, NS_OK);
rv = newPath->AppendNative(NS_LITERAL_CSTRING("blacklisted_dir"));
ASSERT_EQ(rv, NS_OK);
rv = newPath->Exists(&exists);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
nsAutoCString trickyPath(tempPath);
trickyPath.AppendLiteral("/allowed/../blacklisted_dir/file");
rv = newPath->InitWithNativePath(trickyPath);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
// Check that we can't construct a path that is functionally the same
// as the blacklisted one and bypasses the filter.
trickyPath = tempPath;
trickyPath.AppendLiteral("/./blacklisted_dir/file");
rv = newPath->InitWithNativePath(trickyPath);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
trickyPath = tempPath;
trickyPath.AppendLiteral("//blacklisted_dir/file");
rv = newPath->InitWithNativePath(trickyPath);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
trickyPath.Truncate();
trickyPath.AppendLiteral("//");
trickyPath.Append(tempPath);
trickyPath.AppendLiteral("/blacklisted_dir/file");
rv = newPath->InitWithNativePath(trickyPath);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
trickyPath.Truncate();
trickyPath.AppendLiteral("//");
trickyPath.Append(tempPath);
trickyPath.AppendLiteral("//blacklisted_dir/file");
rv = newPath->InitWithNativePath(trickyPath);
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
// Check that if the blacklisted string is a directory, we only block access
// to subresources, not the directory itself.
nsAutoCString blacklistDirPath(blackListPath);
blacklistDirPath.Append("/");
rv = Preferences::SetCString("network.file.path_blacklist", blacklistDirPath);
ASSERT_EQ(rv, NS_OK);
FilePreferences::InitPrefs();
// This should work, since we only block subresources
rv = blacklistedDir->Exists(&exists);
ASSERT_EQ(rv, NS_OK);
rv = blacklistedDir->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
}

Просмотреть файл

@ -76,6 +76,11 @@ if CONFIG['OS_TARGET'] == 'WINNT':
UNIFIED_SOURCES += [
'TestFilePreferencesWin.cpp',
]
else:
UNIFIED_SOURCES += [
'TestFilePreferencesUnix.cpp',
]
if CONFIG['WRAP_STL_INCLUDES'] and CONFIG['CC_TYPE'] != 'clang-cl':
UNIFIED_SOURCES += [