Phase 14: Security + Storage#
Implement the Same-Origin Policy (SOP) as the foundational security primitive for the browser engine.
Background#
The Same-Origin Policy restricts how a document or script loaded from one origin can interact with resources from another origin. An origin is defined as the tuple (scheme, host, port). The Url::origin() method in crates/url/src/lib.rs already derives Origin::Tuple and Origin::Opaque — this issue builds the enforcement layer on top.
Requirements#
Origin comparison#
- Implement
Origin::same_origin(&self, other: &Origin) -> bool— two tuple origins match iff scheme, host, and port are all equal; opaque origins are never same-origin (even with themselves) - Normalize default ports during comparison (http/80, https/443, etc.)
Security checks in resource loading#
- Add an
originfield to the resource loader context so the loader knows the requesting document's origin - Before completing a fetch for scripts, stylesheets, and XHR/Fetch API responses, check whether the response origin matches the requestor origin
- If origins differ and no CORS headers are present, block the response (return a network error)
- Allow cross-origin loads for
<img>(images are opaque by default) and navigation (top-level page loads)
DOM access restrictions#
- Prevent cross-origin
<iframe>content from accessing the parent document's DOM (and vice versa) — return a security error from the JS bridge when a script tries to accesscontentDocumentorcontentWindow.documentacross origins document.domainsetter: allow relaxing origin to a superdomain (per spec, but mark as deprecated)
Integration points#
crates/url: addOrigin::same_origin()method and testscrates/browser/src/loader.rs: enforce SOP on subresource fetchescrates/js: enforce SOP in DOM access bridge
Acceptance Criteria#
-
Origin::same_origin()passes unit tests covering: same tuple, different port, different scheme, opaque origins, default port normalization - Cross-origin script/stylesheet/fetch loads are blocked when no CORS headers present
- Cross-origin image loads succeed (opaque response)
- Top-level navigation is unrestricted
-
cargo clippy --workspace -- -D warningspasses -
cargo test --workspacepasses