Security
Checked against 2994ef5 on 2026-11-10 by Capy (review pass: Subagent 124 and fable). The files it covers are unchanged since.
What the failing CORS tests mean
Section titled “What the failing CORS tests mean”The banner counts WPT’s cors/ directory. Most of its tests are older
.htm pages whose shared cors/support.js relies on a server template
niso’s test runner doesn’t fill in, so they stop with a syntax error before
testing anything and count as failures that say nothing about CORS. Of the
failures that do test CORS, here and in fetch/api/cors/, two fail open,
allowing more than a browser would:
- A malformed
Access-Control-Expose-Headers(bb-8, no no) should expose no header. niso drops the invalid entries and exposes the valid ones the server named (listincrates/niso-core/src/net/fetch.rs;cors/access-control-expose-headers-parsing.window.js). - A cached preflight outlives a network error. When a request fails with
a network error, a browser drops the preflight it cached for it; niso keeps
it until it expires, so the next request goes out without a new preflight
(
fetch/api/cors/cors-preflight-cache.any.js).
The others found fail closed: a credentialed request carries no cookie,
because niso has none (cors-cookies.any.js), and a header value with a
control character in it is refused by the HTTP client, so the request fails
rather than going out without a preflight
(cors-preflight-not-cors-safelisted.any.js). Not examined:
cors/preflight-cache-partitioning.sub.window.js, which fails with
TypeError: not a function, and the tests that time out
(cors/image-tainting-in-cross-origin-iframe.sub.html,
fetch/api/cors/cors-keepalive.any.js). Each result is in
tests/wpt/expectations.json.
A niso page is part of its app. It isn’t sandboxed from the app the way a WebView’s content is: native modules, the host’s loader and storage are the app’s own. So the security questions that matter are about what a page can learn from other origins, and niso answers those the way browsers do.
What niso does
Section titled “What niso does”- CORS: every cross-origin read is checked. A response from another
origin that doesn’t pass CORS is opaque: a no-cors
fetchgets an opaque response with no body, headers or status, and an<img>or<script>can use it without the page reading it. The same rule decides the rest: a canvas that draws an opaque image can’t be read back, a classic script from another origin reports only “Script error.”, cross-origin module scripts and fonts need CORS, and another origin’s style sheet rules can’t be read. - Failures say nothing: every failed
fetchrejects withTypeError("Failed to fetch"), whatever the reason, so a page can’t scan ports or read headers through error messages. - Mixed content: a page delivered over HTTPS can’t make plain HTTP requests (images are upgraded), except to the machine itself.
- Request rules: forbidden methods and headers (
Cookie,Origin,Referer,Host, …) are refused in Rust, for every caller including C.
What it doesn’t do
Section titled “What it doesn’t do”- No credentials: no cookies, HTTP authentication or client certificates. A page can’t read a response tailored to a user’s session, because there’s no session; it can still reach whatever the device’s network reaches, including the LAN.
- No Content Security Policy, CORP, COEP or COOP.
- No isolation between origins in the process: every origin’s scripts share one process. The guarantees hold against script using web APIs, not against an exploit of the JS engine or a Spectre-style side channel.
- No Private Network Access checks and no DNS rebinding protection.
Load only code you trust into a niso app, as you would into any native app. The threat model, with every rule and where it’s enforced, is Cross-origin reads.