privacy pass: degoogle, CSP, referrer

- Replace Google Fonts with system font stacks across all themes
- Add Referrer-Policy, X-Content-Type-Options, X-Frame-Options, CSP headers
- Add rel="noreferrer noopener" on all outbound links
- Add no-referrer and dns-prefetch-control meta tags to all themes
- Clean tracking params on outbound links from trusted/remote sources
- Remove Google domains from CSP whitelists
This commit is contained in:
lichenblankie 2026-04-08 10:11:57 -07:00
parent 9738d28b60
commit a9f426132e
6 changed files with 285 additions and 275 deletions

View file

@ -123,6 +123,14 @@ class GatewayHandler(BaseHTTPRequestHandler):
self.send_response(resp["status"])
self.send_header("Content-Type", resp.get("content_type", "text/html; charset=utf-8"))
self.send_header("Referrer-Policy", "no-referrer")
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("X-Frame-Options", "DENY")
self.send_header("Content-Security-Policy",
"default-src 'self'; "
"style-src 'self' 'unsafe-inline'; "
"script-src 'self' 'unsafe-inline'; "
"img-src 'self' data:")
for k, v in resp.get("headers", {}).items():
self.send_header(k, v)
self.end_headers()

View file

@ -245,7 +245,7 @@ def handle_search(query):
snip_html = f'<br>{esc(r["summary"])}' if r["summary"] else ""
result_html += (
f'<div class="result">'
f'<a href="{esc(r["url"])}">{esc(r["title"])}</a><br>'
f'<a href="{esc(r["url"])}" rel="noreferrer noopener">{esc(r["title"])}</a><br>'
f'<small>{esc(r["url"])}</small>'
f'{snip_html}'
f'{note_html}{tags_html}'
@ -276,7 +276,7 @@ def handle_search(query):
items = ""
for l in trusted:
items += (
f'<li><a href="{esc(l["url"])}">{esc(l["label"])}</a> '
f'<li><a href="{esc(clean_url(l["url"]))}" rel="noreferrer noopener">{esc(l["label"])}</a> '
f'<small>— from {esc(l["source_title"])}</small></li>'
)
trusted_html = (
@ -311,8 +311,8 @@ def handle_search(query):
for r in items:
note_html = f' — <em>{esc(r["note"])}</em>' if r["note"] else ""
source_items += (
f'<li><a href="{esc(r["url"])}">{esc(r["title"])}</a>'
f'{note_html} <small>({esc(r["url"])})</small></li>'
f'<li><a href="{esc(clean_url(r["url"]))}" rel="noreferrer noopener">{esc(r["title"])}</a>'
f'{note_html} <small>({esc(clean_url(r["url"]))})</small></li>'
)
remote_html += (
f'<details class="remote" open>'
@ -473,7 +473,7 @@ def handle_add_manual_submit(body):
# Log error but don't fail the whole operation
print(f"Error generating embeddings: {e}")
return handle_add_form(f'Added manually: <a href="{esc(url)}">{esc(manual_title)}</a>')
return handle_add_form(f'Added manually: <a href="{esc(url)}" rel="noreferrer noopener">{esc(manual_title)}</a>')
finally:
return_db(db)
@ -500,7 +500,7 @@ def handle_pages(query=None):
tags_html = f' {tag_links}'
items += (
f'<li>{esc(r["title"])}{note_html}{tags_html} '
f'<small>(<a href="{esc(r["url"])}">{esc(r["url"])}</a>)</small> '
f'<small>(<a href="{esc(r["url"])}" rel="noreferrer noopener">{esc(r["url"])}</a>)</small> '
f'<a href="/edit/{r["id"]}">edit</a> '
f'<a href="/delete/{r["id"]}">remove</a></li>'
)
@ -700,7 +700,7 @@ def handle_style_form(msg=""):
f"<small>Default: reticulum.derickphan.com:4242</small><br>"
f'<input name="transport_host" value="{esc(transport_host)}" placeholder="hostname" size="30">'
f' <input name="transport_port" value="{esc(transport_port)}" placeholder="port" size="6"><br>'
f'<p><a href="https://rmap.world/" target="_blank">discover more nodes</a></p><br>'
f'<p><a href="https://rmap.world/" target="_blank" rel="noreferrer noopener">discover more nodes</a></p><br>'
f"<h2>search</h2>"
f"<h3>ai</h3>"
f'<label><input type="checkbox" name="semantic_search" value="1"{semantic_checked} '
@ -851,7 +851,7 @@ def handle_tag_browse(tag_name, query=None):
tag_links = " ".join(f'<a href="/tags/{esc(t)}">[{esc(t)}]</a>' for t in tags)
items += (
f'<li>{esc(r["title"])}{note_html} {tag_links} '
f'<small>(<a href="{esc(r["url"])}">{esc(r["url"])}</a>)</small></li>'
f'<small>(<a href="{esc(r["url"])}" rel="noreferrer noopener">{esc(r["url"])}</a>)</small></li>'
)
finally:
return_db(db)
@ -1397,8 +1397,7 @@ def dispatch_request(data):
resp["headers"]["Content-Security-Policy"] = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline'; "
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; "
"font-src 'self' https://fonts.gstatic.com; "
"style-src 'self' 'unsafe-inline'; "
"img-src * data:; "
"frame-ancestors 'none'; "
"form-action 'self'; "

View file

@ -7,13 +7,13 @@ def esc(s):
DEFAULT_TEMPLATE = "<html>\n<head>\n</head>\n<body>\n{{content}}\n</body>\n</html>"
DEFAULT_TEMPLATE = "<html>\n<head>\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta http-equiv=\"x-dns-prefetch-control\" content=\"off\">\n</head>\n<body>\n{{content}}\n</body>\n</html>"
def _default_template():
name = esc(get_setting("site_name", "tinyweb"))
return (
"<html>\n<head>\n</head>\n<body>\n"
'<html>\n<head>\n<meta name="referrer" content="no-referrer">\n<meta http-equiv="x-dns-prefetch-control" content="off">\n</head>\n<body>\n'
f'<p><b><a href="/">{name}</a></b>'
' | <a href="/">search</a> | <a href="/pages">browse</a>'
' | <a href="/tags">tags</a> | <a href="/subscriptions">subscriptions</a>'

View file

@ -3,15 +3,16 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<meta http-equiv="x-dns-prefetch-control" content="off">
<style>
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,400;0,600;0,700;1,400&family=Fira+Code:wght@400;500&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { min-height: 100vh; }
body {
font-family: 'Nunito', -apple-system, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.65;
color: #e0d8c8;
@ -81,7 +82,7 @@
right: 10px;
z-index: 900;
pointer-events: none;
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
display: flex;
flex-direction: column;
align-items: flex-end;
@ -289,7 +290,7 @@
}
nav .site {
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.85rem;
font-weight: 500;
color: #f0d878;
@ -383,7 +384,7 @@
border-radius: 4px;
padding: 0.6rem 0.85rem;
color: #d0c8b8;
font-family: 'Nunito', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 0.95rem;
transition: border-color 0.2s, box-shadow 0.3s;
}
@ -400,7 +401,7 @@
border-radius: 4px;
padding: 0.6rem 1.1rem;
color: #a098b0;
font-family: 'Nunito', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 0.88rem;
transition: all 0.2s;
}
@ -443,7 +444,7 @@
.tags { margin-top: 0.3rem; }
.tag, .tags a {
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.7rem;
color: #8878a0;
border: 1px solid rgba(255,255,255,0.08);
@ -486,7 +487,7 @@
/* code */
pre {
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.8rem;
background: rgba(15, 10, 40, 0.5);
border: 1px solid rgba(255,255,255,0.08);
@ -498,7 +499,7 @@
}
code {
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.82rem;
background: rgba(15, 10, 40, 0.4);
border-radius: 3px;
@ -513,7 +514,7 @@
border-radius: 4px;
padding: 0.7rem 0.9rem;
color: #d0c8b8;
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.8rem;
line-height: 1.6;
resize: vertical;
@ -547,7 +548,7 @@
hr { border: none; border-top: 1px solid rgba(255,255,255,0.06); margin: 1rem 0; }
small {
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.7rem;
color: #5a5070;
}
@ -562,7 +563,7 @@
}
footer .clock {
font-family: 'Fira Code', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.72rem;
color: #3a3050;
margin-top: 0.25rem;

View file

@ -3,13 +3,14 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<meta http-equiv="x-dns-prefetch-control" content="off">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'IBM Plex Sans', -apple-system, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.65;
color: #c8c8c8;
@ -99,7 +100,7 @@
}
nav .site {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.85rem;
font-weight: 500;
color: #e8e8e8;
@ -193,7 +194,7 @@
border-radius: 4px;
padding: 0.6rem 0.85rem;
color: #d0d0d0;
font-family: 'IBM Plex Sans', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 0.95rem;
transition: border-color 0.2s, box-shadow 0.3s;
}
@ -210,7 +211,7 @@
border-radius: 4px;
padding: 0.6rem 1.1rem;
color: #999;
font-family: 'IBM Plex Sans', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 0.88rem;
transition: all 0.2s;
}
@ -253,7 +254,7 @@
.tags { margin-top: 0.3rem; }
.tag, .tags a {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.7rem;
color: #555;
border: 1px solid #252525;
@ -296,7 +297,7 @@
/* code */
pre {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.8rem;
background: #151515;
border: 1px solid #232323;
@ -308,7 +309,7 @@
}
code {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.82rem;
background: #1a1a1a;
border-radius: 3px;
@ -323,7 +324,7 @@
border-radius: 4px;
padding: 0.7rem 0.9rem;
color: #c8c8c8;
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.8rem;
line-height: 1.6;
resize: vertical;
@ -357,7 +358,7 @@
hr { border: none; border-top: 1px solid #1e1e1e; margin: 1rem 0; }
small {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.7rem;
color: #484848;
}
@ -372,7 +373,7 @@
}
footer .clock {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.72rem;
color: #282828;
margin-top: 0.25rem;

View file

@ -3,8 +3,9 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<meta http-equiv="x-dns-prefetch-control" content="off">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box;
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Ccircle cx='10' cy='10' r='3' fill='none' stroke='%23557766' stroke-width='1.5'/%3E%3Ccircle cx='10' cy='10' r='1' fill='%2377aa88'/%3E%3C/svg%3E") 10 10, default;
@ -13,7 +14,7 @@
html, body { min-height: 100vh; }
body {
font-family: 'IBM Plex Sans', -apple-system, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.65;
color: #9ab4b8;
@ -65,7 +66,7 @@
}
nav .site {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.85rem;
font-weight: 500;
color: #b0ccc4;
@ -158,7 +159,7 @@
border-radius: 4px;
padding: 0.6rem 0.85rem;
color: #90b4ac;
font-family: 'IBM Plex Sans', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 0.95rem;
transition: border-color 0.2s, box-shadow 0.3s;
}
@ -175,7 +176,7 @@
border-radius: 4px;
padding: 0.6rem 1.1rem;
color: #5a7880;
font-family: 'IBM Plex Sans', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 0.88rem;
transition: all 0.2s;
}
@ -240,7 +241,7 @@
.tags { margin-top: 0.3rem; }
.tag, .tags a {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.7rem;
color: #3a5e55;
border: 1px solid rgba(40, 70, 60, 0.35);
@ -273,7 +274,7 @@
li a:hover { border-bottom: 1px solid rgba(80, 130, 110, 0.4); }
pre {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.8rem;
background: rgba(6, 14, 16, 0.6);
border: 1px solid rgba(30, 55, 50, 0.3);
@ -285,7 +286,7 @@
}
code {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.82rem;
background: rgba(8, 18, 22, 0.6);
border-radius: 3px;
@ -299,7 +300,7 @@
border-radius: 4px;
padding: 0.7rem 0.9rem;
color: #9ab4b8;
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.8rem;
line-height: 1.6;
resize: vertical;
@ -330,7 +331,7 @@
hr { border: none; border-top: 1px solid rgba(30, 55, 50, 0.3); margin: 1rem 0; }
small {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.7rem;
color: #28454e;
}
@ -344,7 +345,7 @@
}
footer .clock {
font-family: 'IBM Plex Mono', monospace;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', Menlo, Consolas, monospace;
font-size: 0.72rem;
color: #162a30;
margin-top: 0.25rem;