-
-
Notifications
You must be signed in to change notification settings - Fork 38
feat: remote module security #331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a security gating system for remote (HTTP/HTTPS) ES module loading in production builds. The feature allows developers to enable remote module imports via a security configuration section, with optional URL allowlisting for restricting module sources.
Key Changes:
- Added security configuration support in
nativescript.config(package.json) withallowRemoteModulesflag and optionalremoteModuleAllowlist - Implemented security checks at all HTTP module loading points in the runtime
- Added comprehensive test suite for security behavior
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| TestRunner/app/tests/index.js | Added import for new RemoteModuleSecurityTests |
| TestRunner/app/tests/RemoteModuleSecurityTests.js | New comprehensive test suite covering debug mode behavior, security configuration, URL allowlist matching, and edge cases |
| TestRunner/app/package.json | Added security configuration with allowRemoteModules flag and allowlist for testing |
| NativeScript/runtime/DevFlags.h | Added public API declarations for security config functions (IsRemoteModulesAllowed, IsRemoteUrlAllowed, InitializeSecurityConfig) |
| NativeScript/runtime/DevFlags.mm | Implemented security configuration parsing and URL allowlist prefix matching logic |
| NativeScript/runtime/ModuleInternalCallbacks.mm | Integrated security checks at all HTTP module loading entry points (absolute URLs, relative URLs from HTTP referrers, embedded URLs, dynamic imports) |
Comments suppressed due to low confidence (1)
NativeScript/runtime/ModuleInternalCallbacks.mm:1202
- Bug: After throwing a security exception at line 1165, the lambda returns
false, which allows execution to continue at line 1204 and beyond. This is inconsistent with other security checks in this file (e.g., lines 746-753, 875-882, 1050-1057) which immediately return an emptyMaybeLocal<v8::Module>()after throwing. The function should check if an exception was thrown and return immediately, or the lambda should signal an error differently so the caller at line 1191 can detect it and return early.
// Security check: block if remote modules not allowed
if (!IsHttpUrlAllowedForLoading(tail)) {
if (IsScriptLoadingLogEnabled()) {
Log(@"[resolver][security] blocked embedded remote module: %s", tail.c_str());
}
std::string msg = GetRemoteModuleBlockedMessage(tail);
isolate->ThrowException(v8::Exception::Error(tns::ToV8String(isolate, msg.c_str())));
return false;
}
if (IsScriptLoadingLogEnabled()) { Log(@"[resolver][http-embedded] %s -> %s", p.c_str(), tail.c_str()); }
std::string key = CanonicalizeHttpUrlKey(tail);
auto itExisting = g_moduleRegistry.find(key);
if (itExisting != g_moduleRegistry.end()) {
v8::Local<v8::Module> existing = itExisting->second.Get(isolate);
if (!existing.IsEmpty() && existing->GetStatus() != v8::Module::kErrored) {
return !v8::MaybeLocal<v8::Module>(existing).IsEmpty();
}
}
std::string body; std::string ct; int status = 0;
if (HttpFetchText(tail, body, ct, status) && !body.empty()) {
v8::MaybeLocal<v8::Module> m = CompileModuleForResolveRegisterOnly(isolate, context, body, key);
if (!m.IsEmpty()) {
v8::Local<v8::Module> mod; if (m.ToLocal(&mod)) { return true; }
}
}
if (RuntimeConfig.IsDebug) {
std::string msg = "HTTP embedded import failed: " + tail;
isolate->ThrowException(v8::Exception::Error(tns::ToV8String(isolate, msg.c_str())));
}
return false;
};
if (rerouteHttpIfEmbedded(absPath)) {
// Return the module from registry; V8 will pick it up
std::string key = CanonicalizeHttpUrlKey(absPath.substr(absPath.find("http")));
auto itExisting = g_moduleRegistry.find(key);
if (itExisting != g_moduleRegistry.end()) {
v8::Local<v8::Module> existing = itExisting->second.Get(isolate);
if (!existing.IsEmpty()) {
return v8::MaybeLocal<v8::Module>(existing);
}
}
// If not present, fall through to normal checks
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Security gating system for remote (HTTP/HTTPS) ES module loading in production.
securitysectionnativescript.config, enabling remote modules and specifying an allowlist of URL prefixes for permitted remote module sources.In development, defaults to always enabled.
In production, only enabled via explicit developer approval in nativescript.config settings:
If the optional
remoteModuleAllowlistis omitted, it allows any url.Docs added: https://github.com/NativeScript/docs/pull/191/files