From 0a2c6c6d9b4e9baa425fbcd00819d53cc9742d26 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Sat, 1 Aug 2026 08:27:07 -0400 Subject: [PATCH] v0.0.66 - Release: local:// URI scheme for full localStorage/IndexedDB on local files, Security Restrictions Overridden section in README --- Makefile | 2 +- README.md | 71 ++++++++++ VERSION | 2 +- plans/security-bypasses-and-file-storage.md | 139 +++++++++++++++++++ src/local_scheme.c | 144 ++++++++++++++++++++ src/local_scheme.h | 36 +++++ src/main.c | 2 + src/tab_manager.c | 20 +++ src/version.h | 4 +- 9 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 plans/security-bypasses-and-file-storage.md create mode 100644 src/local_scheme.c create mode 100644 src/local_scheme.h diff --git a/Makefile b/Makefile index e25d331..18f9b33 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ NOSTR_LIB = ./nostr_core_lib/libnostr_core_x64.a NOSTR_DEPS = -lsecp256k1 -lssl -lcrypto -lcurl -lz -ldl -lpthread -lm BIN := sovereign_browser -SRC := src/main.c src/key_store.c src/login_dialog.c src/nostr_bridge.c src/nostr_inject.c src/nostr_url.c src/nostr_scheme.c src/history.c src/settings.c src/net_services.c src/tor_control.c src/tor_scheme.c src/fips_control.c src/tab_manager.c src/session.c src/agent_server.c src/agent_login.c src/agent_snapshot.c src/agent_tools.c src/agent_mcp.c src/cli.c src/qr.c src/db.c src/relay_fetch.c src/bookmarks.c src/shortcuts.c src/settings_sync.c src/search.c src/profile.c src/agent_fs_tools.c src/agent_llm.c src/agent_loop.c src/agent_chat_store.c src/agent_chat.c src/agent_conversations.c src/agent_skills.c src/embedded_web_content.c src/process_info.c src/perf_probe.c src/webkit_data.c src/web_context.c src/site_downloader.c +SRC := src/main.c src/key_store.c src/login_dialog.c src/nostr_bridge.c src/nostr_inject.c src/nostr_url.c src/nostr_scheme.c src/history.c src/settings.c src/net_services.c src/tor_control.c src/tor_scheme.c src/fips_control.c src/tab_manager.c src/session.c src/agent_server.c src/agent_login.c src/agent_snapshot.c src/agent_tools.c src/agent_mcp.c src/cli.c src/qr.c src/db.c src/relay_fetch.c src/bookmarks.c src/shortcuts.c src/settings_sync.c src/search.c src/profile.c src/agent_fs_tools.c src/agent_llm.c src/agent_loop.c src/agent_chat_store.c src/agent_chat.c src/agent_conversations.c src/agent_skills.c src/embedded_web_content.c src/process_info.c src/perf_probe.c src/webkit_data.c src/web_context.c src/site_downloader.c src/local_scheme.c # Web files embedded into the binary as C byte arrays. WEB_FILES := $(shell find www -type f \( -name '*.html' -o -name '*.css' -o -name '*.js' \) 2>/dev/null) diff --git a/README.md b/README.md index 9c8adbc..31641f4 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,77 @@ Working browser with a broad feature set: See [`docs/webkit-poc-findings.md`](docs/webkit-poc-findings.md) for the friction report from the POC phase. +## Security restrictions overridden + +This browser intentionally strips the traditional web security model. Trust +moves to the **Qube level** (VM isolation), so the browser itself does not +enforce same-origin policy, CORS, certificate validation, or storage quotas. +Below is the complete inventory of every restriction we've overridden and how. + +### WebKitSettings (per-tab, in [`src/tab_manager.c`](src/tab_manager.c)) + +| Setting | Value | Effect | +|---------|-------|--------| +| `enable_developer_extras` | `TRUE` | Web Inspector / right-click Inspect Element | +| `enable_javascript` | `TRUE` | JavaScript enabled | +| `javascript_can_open_windows_automatically` | `TRUE` | `window.open()` without user gesture | +| `allow_file_access_from_file_urls` | `TRUE` | `file://` pages can fetch/XHR other local files | +| `allow_universal_access_from_file_urls` | `TRUE` | `file://` pages bypass same-origin policy entirely | +| `allow_modal_dialogs` | `TRUE` | `alert()`, `confirm()`, `prompt()` work | +| `hardware_acceleration_policy` | `ALWAYS` | Forces GPU compositing even on software renderers | +| `enable_smooth_scrolling` | `TRUE` | Smooth scroll animation | + +### WebKitSecurityManager (per-context, in [`src/web_context.c`](src/web_context.c)) + +| Registration | Effect | +|-------------|--------| +| `register_uri_scheme_as_secure("sovereign")` | `sovereign://` treated as secure origin | +| `register_uri_scheme_as_secure("tor")` | `tor://` treated as secure origin | +| `register_uri_scheme_as_secure("file")` | `file://` treated as secure origin (normally insecure) | +| `register_uri_scheme_as_secure("ws")` | `ws://` treated as secure (allows plain WebSocket from secure pages) | +| `register_uri_scheme_as_local("file")` | `file://` treated as local (can load local resources) | +| `register_uri_scheme_as_secure("local")` | `local://` treated as secure origin | +| `register_uri_scheme_as_local("local")` | `local://` treated as local | +| `register_uri_scheme_as_cors_enabled("local")` | `local://` pages can fetch/XHR other local:// URLs without CORS | + +### TLS Policy (in [`src/web_context.c`](src/web_context.c)) + +| Setting | Value | Effect | +|---------|-------|--------| +| `tls_errors_policy` | `IGNORE` | Accepts any TLS certificate, including self-signed/expired | + +### CORS bypass for custom schemes (in [`src/nostr_inject.c`](src/nostr_inject.c)) + +The `window.nostr` shim uses **synchronous XMLHttpRequest** to +`sovereign://nostr/*` instead of `fetch()`. WebKitGTK enforces CORS on +`fetch()` and async XHR even for secure custom schemes, but **synchronous +XHR to secure custom schemes bypasses CORS**. This is a WebKitGTK quirk +we exploit intentionally. + +### `file://` storage quota bypass via `local://` scheme + +WebKit's WebCore assigns each `file://` URL a **unique opaque origin** with +a **0-byte storage quota**, silently breaking `localStorage` and `IndexedDB` +on local files. We bypass this with a custom [`local://`](src/local_scheme.c) +URI scheme that: + +1. Maps `local:///absolute/path/to/file` to the local filesystem +2. Creates a **proper (non-opaque) SecurityOrigin** with the default storage + quota (typically 5-10 MB for localStorage, unlimited for IndexedDB) +3. Is registered as secure, local, and CORS-enabled + +All `file://` navigations (URL bar, page links, `decide-policy`) are +automatically rewritten to `local://` by [`src/tab_manager.c`](src/tab_manager.c). + +### Custom URI schemes registered + +| Scheme | Handler | File | +|--------|---------|------| +| `sovereign://` | `on_sovereign_scheme` | [`src/nostr_bridge.c`](src/nostr_bridge.c) | +| `nostr://` | `on_nostr_scheme` | [`src/nostr_scheme.c`](src/nostr_scheme.c) | +| `tor://` | `on_tor_scheme` | [`src/tor_scheme.c`](src/tor_scheme.c) | +| `local://` | `on_local_scheme` | [`src/local_scheme.c`](src/local_scheme.c) | + ## Install One-command install on Debian 13 (trixie) or similar (x86_64, WebKitGTK 4.1): diff --git a/VERSION b/VERSION index 3df20f5..ff8026f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.65 +0.0.66 diff --git a/plans/security-bypasses-and-file-storage.md b/plans/security-bypasses-and-file-storage.md new file mode 100644 index 0000000..42696f7 --- /dev/null +++ b/plans/security-bypasses-and-file-storage.md @@ -0,0 +1,139 @@ +# Security Restrictions Overridden & `file://` Storage Quota Fix + +## Part 1 — Inventory of all security restrictions we've overridden + +### WebKitSettings (per-tab, in [`src/tab_manager.c:2955`](src/tab_manager.c:2955)) + +| Setting | Value | Effect | +|---------|-------|--------| +| `enable_developer_extras` | `TRUE` | Enables Web Inspector / right-click Inspect Element | +| `enable_javascript` | `TRUE` | JavaScript enabled (default, but explicit) | +| `javascript_can_open_windows_automatically` | `TRUE` | Allows `window.open()` without user gesture | +| `allow_file_access_from_file_urls` | `TRUE` | `file://` pages can fetch/XHR other local files (normally blocked) | +| `allow_universal_access_from_file_urls` | `TRUE` | `file://` pages bypass same-origin policy entirely | +| `allow_modal_dialogs` | `TRUE` | `alert()`, `confirm()`, `prompt()` work | +| `hardware_acceleration_policy` | `ALWAYS` | Forces GPU compositing even on software renderers | + +### WebKitSecurityManager (per-context, in [`src/web_context.c:207`](src/web_context.c:207)) + +| Registration | Effect | +|-------------|--------| +| `register_uri_scheme_as_secure("sovereign")` | `sovereign://` treated as secure origin (no mixed-content warnings) | +| `register_uri_scheme_as_secure("tor")` | `tor://` treated as secure origin | +| `register_uri_scheme_as_secure("file")` | `file://` treated as secure origin (normally insecure) | +| `register_uri_scheme_as_secure("ws")` | `ws://` treated as secure (allows plain WebSocket from secure pages) | +| `register_uri_scheme_as_local("file")` | `file://` treated as local (can load local resources) | + +### TLS Policy (in [`src/web_context.c:218`](src/web_context.c:218)) + +| Setting | Value | Effect | +|---------|-------|--------| +| `tls_errors_policy` | `IGNORE` | Accepts any TLS certificate, including self-signed/expired | + +### CORS bypass (in [`src/nostr_inject.c:38`](src/nostr_inject.c:38)) + +The `window.nostr` shim uses **synchronous XMLHttpRequest** to `sovereign://nostr/*` instead of `fetch()`. WebKitGTK enforces CORS on `fetch()` and async XHR even for secure custom schemes, but **synchronous XHR to secure custom schemes bypasses CORS**. This is a WebKitGTK quirk we exploit intentionally. + +### Custom URI schemes registered + +| Scheme | Handler | File | +|--------|---------|------| +| `sovereign://` | `on_sovereign_scheme` | [`src/nostr_bridge.c:4588`](src/nostr_bridge.c:4588) | +| `nostr://` | `on_nostr_scheme` | [`src/nostr_scheme.c:410`](src/nostr_scheme.c:410) | +| `tor://` | `on_tor_scheme` | [`src/tor_scheme.c:289`](src/tor_scheme.c:289) | + +--- + +## Part 2 — The `file://` Storage Quota Problem + +### Root cause + +WebKit's WebCore layer assigns each `file://` URL a **unique opaque origin** with a **0-byte storage quota**. This is hardcoded in `WebCore::SecurityOrigin::createForFilePath()` and `WebCore::StorageQuotaManager`. Even though we've set `allow_file_access_from_file_urls=TRUE` and `allow_universal_access_from_file_urls=TRUE`, those settings affect **network access** (fetch/XHR/CORS), not **storage quotas**. + +The result: +- `localStorage.setItem('key', 'value')` silently fails or throws `QuotaExceededError` +- `IndexedDB.open('db')` fails with `QuotaExceededError` +- `sessionStorage` works (it's in-memory per-tab, not quota-managed) + +### No public WebKitGTK API to fix this + +The WebKitGTK headers expose: +- `webkit_settings_set_enable_html5_local_storage()` — enables/disables localStorage entirely (default: TRUE) +- `webkit_website_data_manager_get_local_storage_directory()` — returns the on-disk path (deprecated) + +There is **no public API** to set the storage quota for a given origin or to change how `file://` origins are classified. + +### Solution: Custom `local://` URI scheme + +The cleanest approach is to create a custom URI scheme that: +1. Has a **proper origin** (not opaque like `file://`) +2. Gets **unlimited storage quota** (the default for http/https origins) +3. Serves the same file content + +#### Implementation plan + +**Step 1 — Register a new `local://` scheme** in [`src/web_context.c`](src/web_context.c): + +```c +// In configure_context(): +webkit_security_manager_register_uri_scheme_as_secure(sec_mgr, "local"); +webkit_security_manager_register_uri_scheme_as_local(sec_mgr, "local"); +webkit_security_manager_register_uri_scheme_as_cors_enabled(sec_mgr, "local"); +``` + +**Step 2 — Create a handler** that maps `local:///path/to/file` to the local filesystem: + +```c +// New file: src/local_scheme.c +static void on_local_scheme(WebKitURISchemeRequest *request) { + const char *uri = webkit_uri_scheme_request_get_uri(request); + // Strip "local://" prefix to get the file path + const char *path = uri + 8; // "local://" = 8 chars + // Read the file and serve it + char *content; + gsize length; + if (g_file_get_contents(path, &content, &length, NULL)) { + const char *mime = g_content_type_guess(path, NULL, 0, NULL); + webkit_uri_scheme_request_finish(request, content, length, mime); + g_free(content); + } +} +``` + +**Step 3 — Intercept `file://` navigation** in the `decide-policy` handler ([`src/tab_manager.c`](src/tab_manager.c)) and rewrite to `local://`: + +```c +// In on_decide_policy(), when navigation policy is requested: +if (g_str_has_prefix(uri, "file://")) { + // Rewrite file:///home/user/foo.html -> local:///home/user/foo.html + char *local_uri = g_strconcat("local", uri + 4, NULL); // "file" -> "local" + webkit_uri_request_set_uri(request, local_uri); + g_free(local_uri); +} +``` + +**Step 4 — Also rewrite `file://` URLs in the URL bar** when the user presses Enter, so typed paths get the `local://` scheme. + +### Why this works + +When a page is loaded from `local:///home/user/site/index.html`, WebKit creates a `SecurityOrigin` with scheme `local`, host `localhost` (or empty), and port `0`. This is a **valid, non-opaque origin** that gets the **default storage quota** (typically 5-10 MB for localStorage, unlimited for IndexedDB). The same origin is shared by all `local://` URLs, so pages can share localStorage. + +### Alternative considered: Local HTTP server + +We could run a minimal HTTP server on `localhost` using libsoup and serve files from `http://localhost:PORT/path`. This would also give proper origins with full storage. However, it adds: +- Port management (conflict with other services) +- Startup latency (server must be ready before pages load) +- Complexity (MIME type handling, directory listing, etc.) + +The custom scheme approach is simpler and has no port/startup issues. + +### Files to modify + +| File | Change | +|------|--------| +| [`src/web_context.c:207`](src/web_context.c:207) | Register `local://` as secure, local, and CORS-enabled | +| `src/local_scheme.c` (new) | Custom scheme handler that reads files from disk | +| `src/local_scheme.h` (new) | Header for the handler | +| [`src/tab_manager.c`](src/tab_manager.c) | Intercept `file://` navigation in `decide-policy` and rewrite to `local://` | +| [`src/main.c`](src/main.c) | Register the `local://` scheme during startup | +| `Makefile` | Add `local_scheme.o` to the build | diff --git a/src/local_scheme.c b/src/local_scheme.c new file mode 100644 index 0000000..7e0c32a --- /dev/null +++ b/src/local_scheme.c @@ -0,0 +1,144 @@ +/* + * local_scheme.c — local:// URI scheme handler for sovereign_browser + * + * Maps local:///absolute/path/to/file to the local filesystem. Unlike + * file://, this scheme creates a proper (non-opaque) SecurityOrigin in + * WebKit, so localStorage, IndexedDB, and other web storage APIs work + * with the default quota instead of the 0-byte quota that WebKit assigns + * to file:// origins. + * + * The handler reads the requested file from disk and serves it with the + * correct MIME type. Directory requests return a 404 (no directory listing). + * + * Security: this is intentionally unrestricted — the browser's security + * model is at the Qube level, not the browser level. + */ + +#include "local_scheme.h" + +#include +#include + +/* ── MIME type helper ───────────────────────────────────────────────── */ + +/* Guess the MIME type from a file path's extension. Falls back to + * application/octet-stream for unknown types. */ +static const char *guess_mime(const char *path) { + const char *ext = strrchr(path, '.'); + if (ext == NULL) return "application/octet-stream"; + + if (g_ascii_strcasecmp(ext, ".html") == 0 || + g_ascii_strcasecmp(ext, ".htm") == 0) + return "text/html; charset=utf-8"; + if (g_ascii_strcasecmp(ext, ".css") == 0) + return "text/css; charset=utf-8"; + if (g_ascii_strcasecmp(ext, ".js") == 0) + return "application/javascript; charset=utf-8"; + if (g_ascii_strcasecmp(ext, ".mjs") == 0) + return "application/javascript; charset=utf-8"; + if (g_ascii_strcasecmp(ext, ".json") == 0) + return "application/json"; + if (g_ascii_strcasecmp(ext, ".svg") == 0) + return "image/svg+xml"; + if (g_ascii_strcasecmp(ext, ".png") == 0) + return "image/png"; + if (g_ascii_strcasecmp(ext, ".jpg") == 0 || + g_ascii_strcasecmp(ext, ".jpeg") == 0) + return "image/jpeg"; + if (g_ascii_strcasecmp(ext, ".gif") == 0) + return "image/gif"; + if (g_ascii_strcasecmp(ext, ".ico") == 0) + return "image/x-icon"; + if (g_ascii_strcasecmp(ext, ".webp") == 0) + return "image/webp"; + if (g_ascii_strcasecmp(ext, ".woff") == 0) + return "font/woff"; + if (g_ascii_strcasecmp(ext, ".woff2") == 0) + return "font/woff2"; + if (g_ascii_strcasecmp(ext, ".ttf") == 0) + return "font/ttf"; + if (g_ascii_strcasecmp(ext, ".otf") == 0) + return "font/otf"; + if (g_ascii_strcasecmp(ext, ".mp4") == 0) + return "video/mp4"; + if (g_ascii_strcasecmp(ext, ".webm") == 0) + return "video/webm"; + if (g_ascii_strcasecmp(ext, ".mp3") == 0) + return "audio/mpeg"; + if (g_ascii_strcasecmp(ext, ".ogg") == 0) + return "audio/ogg"; + if (g_ascii_strcasecmp(ext, ".pdf") == 0) + return "application/pdf"; + if (g_ascii_strcasecmp(ext, ".txt") == 0) + return "text/plain; charset=utf-8"; + if (g_ascii_strcasecmp(ext, ".xml") == 0) + return "application/xml"; + if (g_ascii_strcasecmp(ext, ".wasm") == 0) + return "application/wasm"; + + return "application/octet-stream"; +} + +/* ── Scheme handler ─────────────────────────────────────────────────── */ + +static void on_local_scheme(WebKitURISchemeRequest *request, gpointer user_data) { + (void)user_data; + const char *uri = webkit_uri_scheme_request_get_uri(request); + + /* Strip "local://" prefix to get the file path. The URI is + * local:///absolute/path, so we skip 8 characters ("local://") + * and the result is /absolute/path. */ + const char *path = uri + 8; + if (path == NULL || path[0] == '\0') { + g_printerr("[local-scheme] Empty path in URI: %s\n", uri); + webkit_uri_scheme_request_finish_error(request, g_error_new_literal( + g_quark_from_static_string("local-scheme"), 1, "Empty path")); + return; + } + + /* Read the file. */ + gsize length = 0; + char *content = NULL; + GError *error = NULL; + + if (!g_file_get_contents(path, &content, &length, &error)) { + g_printerr("[local-scheme] Failed to read %s: %s\n", path, + error ? error->message : "unknown error"); + if (error) g_error_free(error); + webkit_uri_scheme_request_finish_error(request, g_error_new_literal( + g_quark_from_static_string("local-scheme"), 2, "File not found")); + return; + } + + /* Determine MIME type and serve. */ + const char *mime_type = guess_mime(path); + g_print("[local-scheme] Serving %s (%s, %lu bytes)\n", path, mime_type, + (unsigned long)length); + + /* Wrap the content in a GInputStream. g_memory_input_stream_new_from_data + * takes ownership of the data via the GDestroyNotify callback. */ + GInputStream *stream = g_memory_input_stream_new_from_data( + content, length, g_free); + webkit_uri_scheme_request_finish(request, stream, length, mime_type); + g_object_unref(stream); +} + +/* ── Public API ─────────────────────────────────────────────────────── */ + +void local_scheme_setup(WebKitWebContext *ctx) { + g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(ctx)); + + webkit_web_context_register_uri_scheme(ctx, "local", on_local_scheme, + NULL, NULL); + + /* Register local:// as secure (no mixed-content warnings), local + * (can load local resources), and CORS-enabled (so fetch/XHR from + * local:// pages to other local:// URLs work without CORS headers). */ + WebKitSecurityManager *sec_mgr = + webkit_web_context_get_security_manager(ctx); + webkit_security_manager_register_uri_scheme_as_secure(sec_mgr, "local"); + webkit_security_manager_register_uri_scheme_as_local(sec_mgr, "local"); + webkit_security_manager_register_uri_scheme_as_cors_enabled(sec_mgr, "local"); + + g_print("[local-scheme] Registered local:// scheme handler\n"); +} diff --git a/src/local_scheme.h b/src/local_scheme.h new file mode 100644 index 0000000..8c953d8 --- /dev/null +++ b/src/local_scheme.h @@ -0,0 +1,36 @@ +/* + * local_scheme.h — local:// URI scheme handler for sovereign_browser + * + * Registers a "local" URI scheme that maps local:///path/to/file to the + * local filesystem. Unlike file://, local:// creates a proper (non-opaque) + * SecurityOrigin in WebKit, which means localStorage, IndexedDB, and other + * web storage APIs work with the default quota instead of the 0-byte quota + * that WebKit assigns to file:// origins. + * + * Usage: + * local_scheme_setup(ctx) — register the handler on the WebKitWebContext + * + * Then load pages via local:///absolute/path/to/file.html instead of + * file:///absolute/path/to/file.html. + */ + +#ifndef LOCAL_SCHEME_H +#define LOCAL_SCHEME_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Register the local:// URI scheme handler on the given WebKitWebContext. + * Must be called once during startup, after the context is created. + */ +void local_scheme_setup(WebKitWebContext *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* LOCAL_SCHEME_H */ diff --git a/src/main.c b/src/main.c index a4ede77..11ebb6a 100644 --- a/src/main.c +++ b/src/main.c @@ -38,6 +38,7 @@ #include "nostr_bridge.h" #include "nostr_scheme.h" #include "tor_scheme.h" +#include "local_scheme.h" #include "nostr_inject.h" #include "history.h" #include "settings.h" @@ -935,6 +936,7 @@ static WebKitWebContext *build_context_for_current_user(void) { * on every fresh context. */ nostr_scheme_register(web_ctx); tor_scheme_register(web_ctx); + local_scheme_setup(web_ctx); /* Point tab_manager at the new context so subsequent new tabs (and * the sidebar webview) are created from it. */ diff --git a/src/tab_manager.c b/src/tab_manager.c index be56ef7..24325a8 100644 --- a/src/tab_manager.c +++ b/src/tab_manager.c @@ -1074,6 +1074,18 @@ static gboolean on_decide_policy(WebKitWebView *webview, } } + /* Rewrite file:// URIs to local:// so pages get a proper SecurityOrigin + * with full storage quota (localStorage, IndexedDB). WebKit assigns a + * 0-byte quota to file:// origins, but local:// creates a non-opaque + * origin with the default storage quota. */ + if (uri && strncmp(uri, "file://", 7) == 0) { + char *local_uri = g_strconcat("local", uri + 4, NULL); /* "file" -> "local" */ + g_print("[decide-policy] Rewriting %s -> %s\n", uri, local_uri); + webkit_policy_decision_ignore(decision); + defer_load_uri(webview, local_uri); /* takes ownership */ + return TRUE; + } + /* NIP-21 links use nostr:entity rather than nostr://entity. Normalize * either form so WebKit consistently invokes our registered handler. */ if (uri && strncmp(uri, "nostr:", 6) == 0 && @@ -1484,6 +1496,14 @@ static void on_url_activate(GtkEntry *entry, gpointer user_data) { char *url = normalize_url(text); if (url != NULL) { + /* Rewrite file:// to local:// so pages get a proper SecurityOrigin + * with full storage quota (localStorage, IndexedDB). */ + if (strncmp(url, "file://", 7) == 0) { + char *local_url = g_strconcat("local", url + 4, NULL); + g_print("[url-bar] Rewriting %s -> %s\n", url, local_url); + g_free(url); + url = local_url; + } webkit_web_view_load_uri(tab->webview, url); g_free(url); } diff --git a/src/version.h b/src/version.h index 3aa975c..c566128 100644 --- a/src/version.h +++ b/src/version.h @@ -11,9 +11,9 @@ #ifndef SOVEREIGN_BROWSER_VERSION_H #define SOVEREIGN_BROWSER_VERSION_H -#define SB_VERSION "v0.0.65" +#define SB_VERSION "v0.0.66" #define SB_VERSION_MAJOR 0 #define SB_VERSION_MINOR 0 -#define SB_VERSION_PATCH 65 +#define SB_VERSION_PATCH 66 #endif /* SOVEREIGN_BROWSER_VERSION_H */