1
0
forked from mirrors/0ad

Disalow importing some files

Not all modules should be able to load all other modules. A predicate
function can to be passed to the `ScriptInterface`. That function
returns whether the module is allowed to loat the module. If no
predicate is passed in no modules can be loaded through that
`ScriptInterface`.
This commit is contained in:
phosit
2025-02-05 15:15:53 +01:00
committed by phosit
parent 6492bc705a
commit 05869454e5
9 changed files with 167 additions and 70 deletions
@@ -0,0 +1 @@
import("restriction/disallowedfile.js");
@@ -0,0 +1 @@
import "restriction/../restriction/disallowedfile.js";
@@ -0,0 +1 @@
import "restriction/disallowedfile.js";
+34 -22
View File
@@ -92,8 +92,14 @@ VfsPath GetBaseFilename(const VfsPath& filename)
return filenames;
}
[[nodiscard]] std::string GetCode(const VfsPath& filePath)
[[nodiscard]] std::string GetCode(const ModuleLoader::AllowModuleFunc& allowModule,
const VfsPath& filePath)
{
if (!allowModule || !allowModule(filePath))
{
throw std::runtime_error{fmt::format("Importing file \"{}\" is disallowed.",
filePath.string8())};
}
if (!VfsFileExists(filePath))
throw std::runtime_error{fmt::format("The file \"{}\" does not exist.", filePath.string8())};
@@ -115,18 +121,19 @@ VfsPath GetBaseFilename(const VfsPath& filename)
}
template<typename Requester>
[[nodiscard]] JSObject* CompileModule(const ScriptRequest& rq, ModuleLoader::RegistryType& registry,
[[nodiscard]] JSObject* CompileModule(const ScriptRequest& rq,
const ModuleLoader::AllowModuleFunc& allowModule, ModuleLoader::RegistryType& registry,
const VfsPath& filePath, Requester&& requester)
{
const VfsPath normalizedPath{filePath.fileSystemPath().lexically_normal().generic_string()};
const auto insertResult = registry.try_emplace(normalizedPath, rq, normalizedPath);
const auto insertResult = registry.try_emplace(normalizedPath, rq, allowModule, normalizedPath);
ModuleLoader::CompiledModule& compiledModule{std::get<1>(*std::get<0>(insertResult))};
compiledModule.AddRequester(std::forward<Requester>(requester));
return compiledModule.m_ModuleObject;
}
[[nodiscard]] JSObject* Resolve(const ScriptRequest& rq, ModuleLoader::RegistryType& registry,
JS::HandleValue referencingModule, JS::HandleObject moduleRequest)
[[nodiscard]] JSObject* Resolve(const ScriptRequest& rq, const ModuleLoader::AllowModuleFunc& allowModule,
ModuleLoader::RegistryType& registry, JS::HandleValue referencingModule,
JS::HandleObject moduleRequest)
{
std::string includeString;
const JS::RootedValue pathValue{rq.cx,
@@ -138,7 +145,7 @@ template<typename Requester>
if (!Script::FromJSProperty(rq, referencingModule, "path", includingModule))
throw std::logic_error{"The importing module doesn't have a \"path\" property."};
return CompileModule(rq, registry, includeString, includingModule);
return CompileModule(rq, allowModule, registry, includeString, includingModule);
}
[[nodiscard]] JSObject* Evaluate(const ScriptRequest& rq, JS::HandleObject mod)
@@ -236,14 +243,16 @@ template<bool reject>
constexpr JSClass callbackClass{"Callback", JSCLASS_HAS_RESERVED_SLOTS(1), &callbackClassOps<reject>};
} // anonymous namespace
ModuleLoader::CompiledModule::CompiledModule(const ScriptRequest& rq, const VfsPath& filePath):
ModuleLoader::CompiledModule::CompiledModule(const ScriptRequest& rq, const AllowModuleFunc& allowModule,
const VfsPath& filePath):
m_ModuleObject(rq.cx)
{
const std::vector<VfsPath> appendices{GetAppendices(filePath)};
const std::string code{std::accumulate(appendices.begin(), appendices.end(), GetCode(filePath),
[](std::string code, const VfsPath& fileToAppend)
const std::string code{std::accumulate(appendices.begin(), appendices.end(),
GetCode(allowModule, filePath),
[&](std::string code, const VfsPath& fileToAppend)
{
return std::move(code) + GetCode(fileToAppend);
return std::move(code) + GetCode(allowModule, fileToAppend);
})};
JS::CompileOptions options{rq.cx};
@@ -294,7 +303,7 @@ void ModuleLoader::CompiledModule::RemoveRequester(Result* toErase)
}), m_Callbacks.end());
}
ModuleLoader::Future::Future(const ScriptRequest& rq, RegistryType& registry, Result& result,
ModuleLoader::Future::Future(const ScriptRequest& rq, ModuleLoader& loader, Result& result,
VfsPath modulePath):
m_Status{Evaluating{{rq.cx, nullptr}, {rq.cx, JS_NewObject(rq.cx, &callbackClass<false>)},
{rq.cx, JS_NewObject(rq.cx, &callbackClass<true>)}}}
@@ -307,7 +316,8 @@ ModuleLoader::Future::Future(const ScriptRequest& rq, RegistryType& registry, Re
// - Accessing values which are not yet exported results in an error. These errors might implicitly be
// dropped.
JS::RootedObject mod{rq.cx, CompileModule(rq, registry, modulePath, result)};
JS::RootedObject mod{rq.cx, CompileModule(rq, loader.m_AllowModule, loader.m_Registry, modulePath,
result)};
JS::RootedObject promise{rq.cx, Evaluate(rq, mod)};
Evaluating& evaluatingStatus{std::get<Evaluating>(m_Status)};
evaluatingStatus.moduleNamespace = JS::GetModuleNamespace(rq.cx, mod);
@@ -412,16 +422,16 @@ ModuleLoader::Result::iterator& ModuleLoader::Result::iterator::operator++(int)
ModuleLoader::Result::Result(const ScriptRequest& rq, const VfsPath& modulePath):
m_Cx{rq.cx},
m_Registry{rq.GetScriptInterface().GetModuleLoader().m_Registry},
m_Loader{rq.GetScriptInterface().GetModuleLoader()},
m_ModulePath{modulePath},
m_Storage{rq, m_Registry, *this, m_ModulePath}
m_Storage{rq, m_Loader, *this, m_ModulePath}
{
}
ModuleLoader::Result::~Result()
{
const auto modIter = m_Registry.find(m_ModulePath);
if (modIter == m_Registry.end())
const auto modIter = m_Loader.m_Registry.find(m_ModulePath);
if (modIter == m_Loader.m_Registry.end())
return;
std::get<1>(*modIter).RemoveRequester(this);
@@ -440,10 +450,11 @@ ModuleLoader::Result::~Result()
void ModuleLoader::Result::Resume()
{
if (m_Storage.IsWaiting())
m_Storage = ModuleLoader::Future{m_Cx, m_Registry, *this, m_ModulePath};
m_Storage = ModuleLoader::Future{m_Cx, m_Loader, *this, m_ModulePath};
}
ModuleLoader::ModuleLoader()
ModuleLoader::ModuleLoader(ModuleLoader::AllowModuleFunc allowModule):
m_AllowModule{std::move(allowModule)}
{
RegisterFileReloadFunc(FileChangedHook, static_cast<void*>(&m_Registry));
}
@@ -485,8 +496,8 @@ ModuleLoader::~ModuleLoader()
try
{
const ScriptRequest rq{cx};
return Resolve(rq, rq.GetScriptInterface().GetModuleLoader().m_Registry, referencingPrivate,
request);
ModuleLoader& loader{rq.GetScriptInterface().GetModuleLoader()};
return Resolve(rq, loader.m_AllowModule, loader.m_Registry, referencingPrivate, request);
}
catch (const std::exception& e)
{
@@ -506,7 +517,8 @@ ModuleLoader::~ModuleLoader()
const ScriptRequest rq{cx};
try
{
JS::RootedObject mod{rq.cx, Resolve(rq, rq.GetScriptInterface().GetModuleLoader().m_Registry,
ModuleLoader& loader{rq.GetScriptInterface().GetModuleLoader()};
JS::RootedObject mod{rq.cx, Resolve(rq, loader.m_AllowModule, loader.m_Registry,
referencingPrivate, moduleRequest)};
JS::RootedObject evaluationPromise{rq.cx, Evaluate(rq, mod)};
return JS::FinishDynamicModuleImport(rq.cx, evaluationPromise, referencingPrivate,
+6 -4
View File
@@ -42,9 +42,10 @@ public:
class Future;
class Result;
using AllowModuleFunc = std::function<bool(const VfsPath&)>;
using RegistryType = std::unordered_map<VfsPath, CompiledModule>;
ModuleLoader();
ModuleLoader(AllowModuleFunc allowModule);
ModuleLoader(const ModuleLoader&) = delete;
ModuleLoader& operator=(const ModuleLoader&) = delete;
ModuleLoader(ModuleLoader&&) = delete;
@@ -72,13 +73,14 @@ private:
[[nodiscard]] static bool DynamicImportHook(JSContext* cx, JS::HandleValue referencingPrivate,
JS::HandleObject moduleRequest, JS::HandleObject promise) noexcept;
AllowModuleFunc m_AllowModule;
RegistryType m_Registry;
};
class ModuleLoader::CompiledModule
{
public:
CompiledModule(const ScriptRequest& rq, const VfsPath& filePath);
CompiledModule(const ScriptRequest& rq, const AllowModuleFunc& allowModule, const VfsPath& filePath);
std::tuple<const std::vector<VfsPath>&,
const std::vector<std::reference_wrapper<Result>>&> GetRequesters() const;
@@ -120,7 +122,7 @@ public:
struct Invalid {};
using Status = std::variant<Evaluating, Fulfilled, Rejected, WaitingForFileChange, Invalid>;
explicit Future(const ScriptRequest& rq, RegistryType& reqistry, Result& result, VfsPath modulePath);
explicit Future(const ScriptRequest& rq, ModuleLoader& reqistry, Result& result, VfsPath modulePath);
Future() = default;
Future(const Future&) = delete;
Future& operator=(const Future&) = delete;
@@ -165,7 +167,7 @@ public:
private:
JSContext* m_Cx;
RegistryType& m_Registry;
ModuleLoader& m_Loader;
VfsPath m_ModulePath;
Future m_Storage;
};
+13 -7
View File
@@ -53,7 +53,8 @@
struct ScriptInterface_impl
{
ScriptInterface_impl(const char* nativeScopeName, ScriptContext& context, JS::Compartment* compartment);
ScriptInterface_impl(const char* nativeScopeName, ScriptContext& context,
JS::Compartment* compartment, std::function<bool(const VfsPath&)> allowModule);
~ScriptInterface_impl();
// Take care to keep this declaration before heap rooted members. Destructors of heap rooted
@@ -303,9 +304,10 @@ bool ScriptInterface::Math_random(JSContext* cx, uint argc, JS::Value* vp)
}
ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, ScriptContext& context,
JS::Compartment* compartment) :
JS::Compartment* compartment, std::function<bool(const VfsPath&)> allowModule) :
m_context(context), m_cx(context.GetGeneralJSContext()), m_glob(context.GetGeneralJSContext()),
m_nativeScope(context.GetGeneralJSContext())
m_nativeScope(context.GetGeneralJSContext()),
m_ModuleLoader{std::move(allowModule)}
{
JS::RealmCreationOptions creationOpt;
// Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement.
@@ -351,8 +353,10 @@ ScriptInterface_impl::~ScriptInterface_impl()
m_context.UnRegisterRealm(JS::GetObjectRealmOrNull(m_glob));
}
ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, ScriptContext& context) :
m(std::make_unique<ScriptInterface_impl>(nativeScopeName, context, nullptr))
ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, ScriptContext& context,
std::function<bool(const VfsPath&)> allowModule) :
m{std::make_unique<ScriptInterface_impl>(nativeScopeName, context, nullptr,
std::move(allowModule))}
{
// Profiler stats table isn't thread-safe, so only enable this on the main thread
if (Threading::IsMainThread())
@@ -366,11 +370,13 @@ ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugN
JS::SetRealmPrivate(JS::GetObjectRealmOrNull(rq.glob), (void*)&m_CmptPrivate);
}
ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, const ScriptInterface& neighbor)
ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName,
const ScriptInterface& neighbor, std::function<bool(const VfsPath& path)> allowModule)
{
ScriptRequest nrq(neighbor);
JS::Compartment* comp = JS::GetCompartmentForRealm(JS::GetCurrentRealmOrNull(nrq.cx));
m = std::make_unique<ScriptInterface_impl>(nativeScopeName, neighbor.GetContext(), comp);
m = std::make_unique<ScriptInterface_impl>(nativeScopeName, neighbor.GetContext(), comp,
std::move(allowModule));
// Profiler stats table isn't thread-safe, so only enable this on the main thread
if (Threading::IsMainThread())
+10 -4
View File
@@ -25,6 +25,7 @@
#include "scriptinterface/ScriptTypes.h"
#include <unordered_map>
#include <functional>
ERROR_GROUP(Scripting);
ERROR_TYPE(Scripting, SetupFailed);
@@ -82,11 +83,13 @@ public:
* @param debugName Name of this interface for CScriptStats purposes.
* @param context ScriptContext to use when initializing this interface.
*/
ScriptInterface(const char* nativeScopeName, const char* debugName, ScriptContext& context);
ScriptInterface(const char* nativeScopeName, const char* debugName, ScriptContext& context,
std::function<bool(const VfsPath&)> allowModule = {});
template<typename Context>
ScriptInterface(const char* nativeScopeName, const char* debugName, Context&& context) :
ScriptInterface(nativeScopeName, debugName, *context)
ScriptInterface(const char* nativeScopeName, const char* debugName, Context&& context,
std::function<bool(const VfsPath&)> allowModule = {}) :
ScriptInterface{nativeScopeName, debugName, *context, std::move(allowModule)}
{
static_assert(std::is_lvalue_reference_v<Context>, "`ScriptInterface` doesn't take ownership "
"of the context.");
@@ -99,8 +102,11 @@ public:
* be placed into, as a scoping mechanism; typically "Engine"
* @param debugName Name of this interface for CScriptStats purposes.
* @param scriptInterface 'Neighbor' scriptInterface to share a compartment with.
* @param allowModule A predicate deciding wheter to import the given
* module.
*/
ScriptInterface(const char* nativeScopeName, const char* debugName, const ScriptInterface& neighbor);
ScriptInterface(const char* nativeScopeName, const char* debugName, const ScriptInterface& neighbor,
std::function<bool(const VfsPath&)> allowModule = {});
~ScriptInterface();
+101 -33
View File
@@ -65,6 +65,16 @@ void ClearFromCache(const VfsPath& path)
ReloadChangedFiles();
}
bool AllowAllPredicate(const VfsPath&)
{
return true;
}
bool DisallowedfilePredicate(const VfsPath& path)
{
return path != "restriction/disallowedfile.js";
}
}
class TestScriptModule : public CxxTest::TestSuite
@@ -90,7 +100,7 @@ public:
void test_StaticImport()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
TestLogger logger;
@@ -103,12 +113,12 @@ public:
void test_Sequential()
{
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
std::ignore = script.GetModuleLoader().LoadModule(rq, "empty.js");
}
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
std::ignore = script.GetModuleLoader().LoadModule(rq, "empty.js");
}
@@ -116,10 +126,10 @@ public:
void test_Stacked()
{
ScriptInterface scriptOuter{"Test", "Test", g_ScriptContext};
ScriptInterface scriptOuter{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rqOuter{scriptOuter};
{
ScriptInterface scriptInner{"Test", "Test", g_ScriptContext};
ScriptInterface scriptInner{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rqInner{scriptInner};
std::ignore = scriptInner.GetModuleLoader().LoadModule(rqInner, "empty.js");
}
@@ -128,7 +138,7 @@ public:
void test_ImportInFunction()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
TestLogger logger;
@@ -141,7 +151,7 @@ public:
void test_NonExistent()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
const TestLogger _;
@@ -151,7 +161,7 @@ public:
void test_EvaluateOnce()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
{
@@ -168,7 +178,7 @@ public:
void test_TopLevelAwaitFinite()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "top_level_await_finite.js");
@@ -181,7 +191,7 @@ public:
void test_TopLevelAwaitInfinite()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "top_level_await_infinite.js");
@@ -194,7 +204,7 @@ public:
void test_MoveFulfilledFuture()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result{script.GetModuleLoader().LoadModule(rq, "empty.js")};
@@ -214,7 +224,7 @@ public:
void test_MoveEvaluatingFuture()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result{script.GetModuleLoader().LoadModule(rq, "top_level_await_finite.js")};
@@ -235,7 +245,7 @@ public:
void test_EvaluateReplacedFuture()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
TestLogger logger;
@@ -255,7 +265,7 @@ public:
void test_TopLevelThrow()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
// To silence the error.
@@ -271,7 +281,7 @@ public:
void test_Export()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "export.js");
@@ -298,7 +308,7 @@ public:
void test_ExportSame()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
{
@@ -322,7 +332,7 @@ public:
void test_ExportIndirect()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
{
@@ -346,7 +356,7 @@ public:
void test_ExportDefaultImmutable()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "export_default/immutable.js");
@@ -363,7 +373,7 @@ public:
void test_ExportDefaultInvalid()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
TestLogger logger;
@@ -375,7 +385,7 @@ public:
void test_ExportDefaultDoesNotWorkAround()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "export_default/does_not_work_around.js");
@@ -394,7 +404,7 @@ public:
void test_ExportDefaultWorksAround()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "export_default/works_around.js");
@@ -411,7 +421,7 @@ public:
void test_ReplaceEvaluatingFuture()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto awaitResult = script.GetModuleLoader().LoadModule(rq, "top_level_await_finite.js");
@@ -430,7 +440,7 @@ public:
void test_DynamicImport()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "dynamic_import.js");
@@ -461,7 +471,7 @@ public:
void test_Meta()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "meta.js");
@@ -481,7 +491,7 @@ public:
void test_Modified()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "modified/base.js");
@@ -500,7 +510,7 @@ public:
{
constexpr int goal{2};
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
int counter{0};
@@ -525,7 +535,7 @@ public:
void test_HotloadWithoutIncrement()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "top_level_await_finite.js");
@@ -537,7 +547,7 @@ public:
void test_HotloadIndipendence()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
// It's intended to be used as in the test above but it's easier to test when it's unrolled.
@@ -576,7 +586,7 @@ public:
void test_HotloadModified()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "empty.js");
@@ -595,7 +605,7 @@ public:
void test_HotloadIndirect()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
const ScriptRequest rq{script};
auto result = script.GetModuleLoader().LoadModule(rq, "indirect.js");
@@ -612,7 +622,7 @@ public:
void test_HotloadUnobserved()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
{
const ScriptRequest rq{script};
@@ -641,7 +651,7 @@ public:
void test_HotloadAfterResultDestruction()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
{
const ScriptRequest rq{script};
@@ -663,7 +673,65 @@ public:
void test_ResultDestructionAfterScriptRequestDestruction()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
ScriptInterface script{"Test", "Test", g_ScriptContext, AllowAllPredicate};
auto _ = script.GetModuleLoader().LoadModule(ScriptRequest{script}, "empty.js");
}
void test_RestrictionNoPredicate()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
const ScriptRequest rq{script};
TS_ASSERT_THROWS_EQUALS(std::ignore = script.GetModuleLoader().LoadModule(rq,
"empty.js"), const std::runtime_error& e, e.what(),
"Importing file \"empty.js\" is disallowed.");
}
void test_RestrictionDirect()
{
ScriptInterface script{"Test", "Test", g_ScriptContext, DisallowedfilePredicate};
const ScriptRequest rq{script};
TS_ASSERT_THROWS_EQUALS(std::ignore = script.GetModuleLoader().LoadModule(rq,
"restriction/disallowedfile.js"), const std::runtime_error& e, e.what(),
"Importing file \"restriction/disallowedfile.js\" is disallowed.");
}
void test_RestrictionIndirect()
{
ScriptInterface script{"Test", "Test", g_ScriptContext, DisallowedfilePredicate};
const ScriptRequest rq{script};
TestLogger logger;
TS_ASSERT_THROWS_EQUALS(std::ignore = script.GetModuleLoader().LoadModule(rq,
"restriction/import.js"), const std::invalid_argument& e, e.what(),
"Unable to link module.");
TS_ASSERT_STR_CONTAINS(logger.GetOutput(),
"Importing file \"restriction/disallowedfile.js\" is disallowed.");
}
void test_RestrictionFancy()
{
ScriptInterface script{"Test", "Test", g_ScriptContext, DisallowedfilePredicate};
const ScriptRequest rq{script};
TestLogger logger;
TS_ASSERT_THROWS_EQUALS(std::ignore = script.GetModuleLoader().LoadModule(rq,
"restriction/fancy_import.js"), const std::invalid_argument& e, e.what(),
"Unable to link module.");
TS_ASSERT_STR_CONTAINS(logger.GetOutput(),
"Importing file \"restriction/disallowedfile.js\" is disallowed.");
}
void test_RestrictionDynamic()
{
ScriptInterface script{"Test", "Test", g_ScriptContext, DisallowedfilePredicate};
const ScriptRequest rq{script};
TestLogger logger;
auto result = script.GetModuleLoader().LoadModule(rq, "restriction/dynamic_import.js");
TS_ASSERT_STR_CONTAINS(logger.GetOutput(),
"Importing file \"restriction/disallowedfile.js\" is disallowed.");
g_ScriptContext->RunJobs();
TS_ASSERT_THROWS_ANYTHING(std::ignore = result.begin()->Get());
}
};