Repository navigation
fix: security hardening - #36
Merged
Merged
Conversation
Engine-variant releases staged the tool binaries and DotNetBridge committed under template/framework unless a manual run set rebuild_framework, so a tag release published binaries built off CI under a CI provenance attestation. Tag releases now always rebuild the shared framework, clear the committed tool binaries first and fail if a tool is missing. windows-napi.yml gets a read-only default token, actions pinned to the SHAs npm_release.yml already uses, and npm provenance for the root and per-triple packages.
Any page in the developer's browser could open a WebSocket to the fixed ws://127.0.0.1:43000/devtools/page/runtime path during `ns debug` and run arbitrary code through the inspector. The handshake now requires a devtools:// Origin or none (VS Code and other CDP clients send none), and both the handshake and /json reject a non-loopback Host, which defeats DNS rebinding.
napi_get_typedarray_info and napi_get_dataview_info returned the ArrayBuffer's base pointer instead of the view's first byte, so native code read and wrote the wrong bytes of a subarray. napi_get_value_string_utf16 and script evaluation used strlen, cutting strings at an embedded NUL.
Without --key-hex every bundle was sealed with the one default key compiled into the public runtime DLL, so a key extracted once opened any app's bundle. `nsbundle_pack --write-key <file>` seals with a fresh random key and writes it beside the project; the template embeds it as the NSBundleKey resource and RuntimeHost passes it to runtime_set_bundle_key before runtime_init. The CLI passes --write-key when no key is given. The container parser also bounds a corrupt entry count's allocation and checks blob offsets without overflow, so a damaged bundle fails to load instead of aborting the app.
A runFullTrust app doesn't run in an AppContainer, so the capability gated nothing and only added a "home or work networks" disclosure to the Store listing. internetClient stays; an app or plugin that wants the capability can add it through the manifest merger.
Marshaling (both the classic and napi engines):
- A pointer comes only from an External, a BigInt or null. A Number or string `handle` or
`__native_ptr` is refused, since JSON can carry those: `{"handle": 1094795585}` passed to
an Object parameter used to be AddRef'd as a COM pointer.
- Interface and class parameters QueryInterface every pointer, boxed primitives included, and
no longer fall back to the raw pointer when that fails, so a string passed as a Uri can't be
called through the wrong vtable. Delegate parameters accept JS delegates the runtime made and
QueryInterface anything else; GenericMethodCall fails instead of calling a slot on the wrong
interface.
- Shapes the call layout can't express (non-byte arrays, received arrays, array returns,
structs larger than the return buffer, JS delegates taking arrays) fail at construction
instead of corrupting the libffi call.
- Struct buffers must cover the struct, serialized structs are padded to their ABI size, and
IReference<T> struct fields are pointer-sized.
- Buffer arguments are rechecked before the call, since parsing a later argument can run JS
that detaches or resizes them.
- Only two-field wrappers are read as a DeclarationFFI, and URL accessors check their receiver,
so a URL passed to WinRT or `instanceof` is no longer read as the wrong type.
Release builds keep app console output out of the Windows Event Log and the on-disk trace log,
and the trace log no longer falls back to a hardcoded user directory.
Remote modules: allowlist entries match by origin and path boundary rather than string
prefix, the response's final URL must also pass the gate after redirects, the gate reads the
entry's package.json for script entries too, and NSWinRT.dynamicImport goes through the native
loader. WebSocket subprotocols must be HTTP tokens and a message is capped at 100 MiB.
Also: crypto.getRandomValues and crypto.randomUUID backed by the OS RNG, and NSWinRT.win32.bind
loads a bare DLL name from the app directory and System32 only.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A security pass before the first major release. It makes the runtime refuse JS data that would otherwise become native pointers or mistyped COM objects, keeps app console output out of persistent logs in release builds, and tightens the debug inspector, remote module loading, source protection and the release pipeline. Each commit stands on its own; they are listed in order below.
Changes
ci: build published variants from source and pin the napi workflow. Tag releases of the engine variants now rebuild the shared tools and DotNetBridge instead of shipping the copies committed undertemplate/framework, and fail if a tool is missing.windows-napi.ymlgets a read-only default token, actions pinned to the SHAsnpm_release.ymluses, and npm provenance.fix(devtools): refuse inspector connections from web pages. The inspector listens on a fixed loopback path duringns debug, and browsers let any page open a WebSocket to localhost. The handshake now requires adevtools://Origin or none (VS Code and other CDP clients send none), and a non-loopbackHostis rejected to stop DNS rebinding.fix(quickjs): honor view offsets and embedded NULs in the napi shim. Typed-array and DataView info returned the buffer's base pointer instead of the view's first byte, and UTF-16 strings and evaluated scripts stopped at an embedded NUL.feat(source-protect): sealapp.nsbundlewith a per-build key. Bundles were sealed with one default key compiled into the public runtime.nsbundle_pack --write-keynow seals with a fresh random key that the template embeds andRuntimeHostpasses toruntime_set_bundle_key; the CLI side is NativeScript/nativescript-cli@8756c1cb1. A corrupt bundle now fails to load instead of aborting the app.chore(template): dropprivateNetworkClientServer. It gates nothing in a full-trust app and only adds a Store disclosure.internetClientstays.fix(runtime): harden marshaling, release logging and remote loading. On both the classic and napi engines:handle/__native_ptr, whichJSON.parsecan produce, is refused.GenericMethodCallno longer calls a slot on the wrong interface.instanceof, argument passing) or as each other.console.logtrace file.NSWinRT.dynamicImportgoes through the native loader.crypto.getRandomValues/crypto.randomUUIDon the OS RNG.NSWinRT.win32.bindloads a bare DLL name from the app directory and System32 only.Behavior changes
handle, or an unsupported array shape now throws a TypeError where it used to crash or misbehave.remoteModuleAllowlistentries match whole path segments:https://cdn.example.com/libno longer allows/library.js.NSWinRT.dynamicImportreads the top-levelsecuritykey from the app's package.json (as the module loader does), notnativescript.security.win32.bind('foo.dll')no longer finds a DLL that is only onPATH; pass a full path.console.logfile; output still goes toOutputDebugStringand stderr.Testing
cargo test -p runtime: 163 passed; the 11 failures are the environment-only baseline (UI thread, dispatcher, ApplicationData). New tests cover each rule above; onmain, the JSON-handle and wrong-type-argument tests crash with an access violation.windows-runtime-integration-testsandruntime-devtools: all pass.windows-napinpm test: 439 passed, 0 failed.packages/test/{interop,enum,security}-check.json QuickJS, Hermes, V8 and JSC: all pass.ns build windows --releasewith source protection, and the CI changes, which take effect on the next tagged release.