Security update: no script attributes

This commit is contained in:
Thomas Mathews 2023-01-20 09:53:33 -08:00
parent 8f14f77576
commit 21ce55f1cd
6 changed files with 134 additions and 66 deletions

View file

@ -98,20 +98,21 @@ th, td {
#nav > div[data-active] img.active {
display: none;
}
#nav > div[data-active="home"] [role="home"] img.inactive,
#nav > div[data-active="explore"] [role="explore"] img.inactive,
#nav > div[data-active="notifications"] [role="notifications"] img.inactive,
#nav > div[data-active="settings"] [role="settings"] img.inactive,
#nav > div[data-active="messages"] [role="dm"] img.inactive {
#nav > div[data-active="home"] [data-view="friends"] img.inactive,
#nav > div[data-active="explore"] [data-view="explore"] img.inactive,
#nav > div[data-active="notifications"] [data-view="notifications"] img.inactive,
#nav > div[data-active="settings"] [data-view="settings"] img.inactive,
#nav > div[data-active="messages"] [data-view="dm"] img.inactive {
display: none;
}
#nav > div[data-active="home"] [role="home"] img.active,
#nav > div[data-active="explore"] [role="explore"] img.active,
#nav > div[data-active="notifications"] [role="notifications"] img.active,
#nav > div[data-active="settings"] [role="settings"] img.active,
#nav > div[data-active="messages"] [role="dm"] img.active {
#nav > div[data-active="home"] [data-view="friends"] img.active,
#nav > div[data-active="explore"] [data-view="explore"] img.active,
#nav > div[data-active="notifications"] [data-view="notifications"] img.active,
#nav > div[data-active="settings"] [data-view="settings"] img.active,
#nav > div[data-active="messages"] [data-view="dm"] img.active {
display: block;
}
#app-icon-logo > img {
width: 36px;
height: 36px;
@ -145,26 +146,26 @@ button.nav > img.icon {
width: 28px;
height: 28px;
}
#gnav button[role="open-gnav"] {
#gnav button[action="toggle-gnav"] {
z-index: var(--zGlobal);
padding: 15px;
}
#gnav.open button[role="home"] {
#gnav.open button[data-view="friends"] {
top: -375px;
}
#gnav.open button[role="explore"] {
#gnav.open button[data-view="explore"] {
top: -300px;
}
#gnav.open button[role="dm"] {
#gnav.open button[data-view="dm"] {
top: -225px;
}
#gnav.open button[role="notifications"] {
#gnav.open button[data-view="notifications"] {
top: -150px;
}
#gnav.open button[role="notifications"] .new-notifications {
#gnav.open button[data-view="notifications"] .new-notifications {
right: 9px;
}
#gnav.open button[role="settings"] {
#gnav.open button[data-view="settings"] {
top: -75px;
}
@ -282,9 +283,6 @@ button.nav > img.icon {
.event-content > .info {
display: inline-block;
}
.event-content > .info button[role="view-event"] {
margin-left: 10px;
}
.username, .thread-id {
font-weight: 800;
font-size: var(--fsReduced);

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#0f0f0f"/>
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; manifest-src 'self'; connect-src 'self' ws: wss:; script-src 'self'; script-src-elem 'self'; script-src-attr 'unsafe-inline'; style-src 'self' fonts.googleapis.com; img-src http: https: data:; media-src *; font-src 'self' fonts.gstatic.com; child-src 'none';" />
content="default-src 'none'; manifest-src 'self'; connect-src 'self' ws: wss:; script-src 'self'; script-src-elem 'self'; script-src-attr 'none'; style-src 'self' fonts.googleapis.com; img-src http: https: data:; media-src *; font-src 'self' fonts.gstatic.com; child-src 'none';" />
<title>Yo, Sup</title>
<link rel="manifest" href="pwa/manifest.json"/>
<link rel="icon" href="icon/icon.svg" type="image/svg+xml"/>
@ -47,13 +47,13 @@
<img class="icon svg" src="icon/logo-inverted.svg"/>
</h1>
<p>The blue bird experience for Nostr.</p>
<button class="action" onclick="signin()">
<button class="action" action="sign-in">
Sign In with Key
<img src="./icon/key.svg" class="icon svg small invert"/>
</button>
<br/>
<br/>
<button class="btn-text" onclick="open_faqs()">
<button class="btn-text" action="open-faqs">
What's Nostr?
</button>
</div>
@ -61,25 +61,25 @@
</div>
<div id="container-app" class="hide">
<nav id="gnav" class="">
<button class="icon" role="open-gnav" title="Open Menu" onclick="toggle_gnav(this)">
<nav id="gnav">
<button class="icon" action="toggle-gnav" title="Open Menu">
<img class="icon svg invert" src="icon/logo.svg"/>
</button>
<button class="icon" role="home" title="Home" onclick="switch_view('friends')">
<button class="icon" action="open-view" data-view="friends" title="Home">
<img class="icon svg invert" src="icon/home.svg"/>
</button>
<button class="icon" role="explore" title="Explore" onclick="switch_view('explore')">
<button class="icon" action="open-view" data-view="explore" title="Explore">
<img class="icon svg invert" src="icon/explore.svg"/>
</button>
<button class="icon" role="dm" title="Direct Messages" onclick="switch_view('dm')">
<button class="icon" action="open-view" data-view="dm" title="Direct Messages">
<img class="icon svg invert" src="icon/messages.svg"/>
<div class="new-notifications hide" role="dm"></div>
</button>
<button class="icon" role="notifications" title="Notifications" onclick="switch_view('notifications')">
<button class="icon" action="open-view" data-view="notifications" title="Notifications">
<img class="icon svg invert" src="icon/notifications.svg"/>
<div class="new-notifications hide" role="activity"></div>
</button>
<button class="icon" role="settings" title="Settings" onclick="switch_view('settings')">
<button class="icon" action="open-view" data-view="settings" title="Settings">
<img class="icon svg invert" src="icon/settings.svg"/>
</button>
</nav>
@ -90,29 +90,29 @@
<div id="app-icon-logo">
<img class="icon svg" title="Damus" src="icon/logo-inverted.svg"/>
</div>
<button role="home" class="nav icon"
title="Home" onclick="switch_view('friends')">
<button action="open-view" data-view="friends" class="nav icon"
title="Home">
<img class="icon svg inactive" src="icon/home.svg"/>
<img class="icon svg active" src="icon/home-active.svg"/>
</button>
<button role="explore" class="nav icon"
title="Explore" onclick="switch_view('explore')"> <img class="icon svg inactive" src="icon/explore.svg"/>
<button action="open-view" data-view="explore" class="nav icon"
title="Explore"> <img class="icon svg inactive" src="icon/explore.svg"/>
<img class="icon svg active" src="icon/explore-active.svg"/>
</button>
<button role="dm" class="nav icon"
title="Direct Messages" onclick="switch_view('dm')">
<button action="open-view" data-view="dm" class="nav icon"
title="Direct Messages">
<img class="icon svg inactive" src="icon/messages.svg"/>
<img class="icon svg active" src="icon/messages-active.svg"/>
<div class="new-notifications hide" role="dm"></div>
</button>
<button role="notifications" class="nav icon"
title="Notifications" onclick="switch_view('notifications')">
<button action="open-view" data-view="notifications"
class="nav icon" title="Notifications">
<img class="icon svg inactive" src="icon/notifications.svg"/>
<img class="icon svg active" src="icon/notifications-active.svg"/>
<div class="new-notifications hide" role="activity"></div>
</button>
<button role="settings" title="Settings" class="nav icon"
onclick="switch_view('settings')">
<button action="open-view" data-view="settings"
title="Settings" class="nav icon">
<img class="icon svg inactive" src="icon/settings.svg"/>
<img class="icon svg active" src="icon/settings-active.svg"/>
</button>
@ -172,7 +172,7 @@
</div>
</div>
<div id="show-new" class="show-new bottom-border hide">
<button onclick="show_new()">
<button action="show-timeline-new">
Show New (<span role="count">0</span>)</button>
</div>
<div id="dms-not-available" class="hide">
@ -248,7 +248,7 @@
<div class="modal closed" id="media-preview">
<div class="media-container">
<img onclick="close_media_preview()" src=""/>
<img action="close-media" src=""/>
</div>
<!-- TODO add loader to media preview -->
</div>
@ -257,7 +257,7 @@
<div id="reply-modal-content" class="modal-content">
<header>
<label>Reply To</label>
<button class="icon" onclick="close_modal(this)">
<button class="icon" action="close-modal">
<img class="icon svg" src="icon/close-modal.svg"/>
</button>
</header>
@ -276,7 +276,7 @@
<div class="modal-content">
<header>
<label>Update Profile</label>
<button class="icon" onclick="close_modal(this)">
<button class="icon" action="close-modal">
<img class="icon svg" src="icon/close-modal.svg"/>
</button>
</header>
@ -285,7 +285,7 @@
<input type="text" class="block w100" name="picture" placeholder="Picture URL"/>
<input type="text" class="block w100" name="nip05" placeholder="nip05"/>
<textarea name="about" class="block w100" placeholder="A bit about you."></textarea>
<button class="action float-right" onclick="click_update_profile()">
<button class="action float-right" action="open-profile-editor">
Update
</button>
</div>
@ -295,7 +295,7 @@
<div class="modal-content">
<header>
<label>Event Details</label>
<button class="icon modal-floating-close-btn" onclick="close_modal(this)">
<button class="icon modal-floating-close-btn" action="close-modal">
<img class="icon svg" src="icon/close-modal.svg"/>
</button>
</header>
@ -306,7 +306,7 @@
</div>
<div id="faqs" class="modal scrollable closed">
<button class="icon modal-floating-close-btn" onclick="close_modal(this)">
<button class="icon modal-floating-close-btn" action="close-modal">
<img class="icon svg" src="icon/close-modal.svg"/>
</button>
<div class="page-content">

View file

@ -19,6 +19,7 @@ const SID_FRIENDS = "friends";
// https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event
addEventListener('DOMContentLoaded', (ev) => {
damus_web_init();
document.addEventListener("click", onclick_any);
});
async function damus_web_init() {
@ -295,4 +296,3 @@ function fetch_friends_history(friends, pool, relay) {
limit: 500,
}], relay);
}

View file

@ -11,7 +11,7 @@ function linkify(text="", show_media=false) {
if (show_media && is_img_url(parsed.pathname)) {
markup = html`
<img class="inline-img clickable" src="${url}"
onclick="open_media_preview('${url}', 'image')"/>`;
action="open-media" data-type="image"/>`;
} else if (show_media && is_video_url(parsed.pathname)) {
markup = html`
<video controls class="inline-img" />

View file

@ -13,8 +13,8 @@ function render_replying_to(model, ev) {
if (!replying_to) {
return html`<span class="replying-to small-txt">
replying in thread
<span class="thread-id clickable"
onclick="open_thread('${ev.refs.reply}')">
<span class="thread-id clickable" action="open-thread"
data-thread-id="${ev.refs.reply}">
${fmt_pubkey(ev.refs.reply)}</span></span>`;
} else {
pubkeys = [replying_to.pubkey];
@ -156,9 +156,9 @@ function render_event_body(model, ev, opts) {
function render_react_onclick(our_pubkey, reacting_to, emoji, reactions) {
const reaction = reactions[our_pubkey]
if (!reaction) {
return html`onclick="send_reply('${emoji}', '${reacting_to}')"`
return html`action="reply" data-emoji="${emoji}" data-to="${reacting_to}"`;
} else {
return html`onclick="delete_post('${reaction.id}')"`
return html`action="delete" data-evid="${reaction.id}"`;
}
}
@ -196,14 +196,15 @@ function render_action_bar(model, ev, opts={}) {
const reaction_id = reaction ? reaction.id : "";
let str = html`<div class="action-bar">`;
if (!shared && event_can_reply(ev)) {
str += html`<button class="icon" title="Reply" onclick="reply_author('${ev.id}')">
str += html`
<button class="icon" title="Reply" action="reply-author" data-evid="${ev.id}">
<img class="icon svg small" src="icon/event-reply.svg"/>
</button>
<button class="icon" title="Reply All" onclick="reply_all('${ev.id}')">
<button class="icon" title="Reply All" action="reply-all" data-evid="${ev.id}">
<img class="icon svg small" src="icon/event-reply-all.svg"/>
</button>
<button class="icon react heart ${ab(liked, 'liked', '')}"
onclick="click_toggle_like(this)"
action="react-like"
data-reaction-id="${reaction_id}"
data-reacting-to="${ev.id}"
title="$${ab(liked, 'Unlike', 'Like')}">
@ -212,27 +213,29 @@ function render_action_bar(model, ev, opts={}) {
</button>`;
}
if (!shared) {
str += html`<button class="icon" title="Share" data-evid="${ev.id}" onclick="click_share(this)">
str += html`<button class="icon" title="Share" data-evid="${ev.id}"
action="share">
<img class="icon svg small" src="icon/event-share.svg"/>
</button>`;
}
str += `
<button class="icon" title="View Thread" role="view-thread"
onclick="open_thread('${thread_root}')">
<button class="icon" title="View Thread" action="open-thread"
data-thread-id="${thread_root}">
<img class="icon svg small" src="icon/open-thread.svg"/>
</button>
<button class="icon" title="View Replies" role="view-replies"
onclick="open_thread('${ev.id}')">
<button class="icon" title="View Replies" action="open-thread"
data-thread-id="${ev.id}">
<img class="icon svg small" src="icon/open-thread-here.svg"/>
</button>
<button class="icon" title="View Event JSON" role="view-event-json"
onclick="on_click_show_event_details('${ev.id}')">
<button class="icon" title="View Event JSON" action="show-event-json"
data-evid="${ev.id}">
<img class="icon svg small" src="icon/event-details.svg"/>
</button>`;
if (can_delete) {
const delete_id = shared ? shared.share_evid : ev.id;
str += html`
<button class="icon" title="Delete" onclick="delete_post_confirm('${delete_id}')">
<button class="icon" title="Delete" action="confirm-delete"
data-evid="${delete_id}">
<img class="icon svg small" src="icon/event-delete.svg"/>
</button>`
}
@ -275,14 +278,14 @@ function render_name(pk, profile, prefix="") {
function render_profile_img(profile, noclick=false) {
const name = fmt_name(profile);
let str = html`class="pfp clickable" onclick="open_profile('${profile.pubkey}')"`;
let str = html`class="pfp clickable" action="open-profile"`;
if (noclick)
str = "class='pfp'";
return html`<img
$${str}
data-pubkey="${profile.pubkey}"
title="${name}"
onerror="this.onerror=null;this.src='${IMG_NO_USER}';"
src="${get_profile_pic(profile)}"/>`
//onerror="this.onerror=null;this.src='${IMG_NO_USER}';"
}

View file

@ -566,3 +566,70 @@ function onclick_toggle_cw(ev) {
const input = el.parentElement.querySelector("input.cw");
input.classList.toggle("hide", !isOn);
}
function onclick_any(ev) {
const el = ev.target;
const action = el.getAttribute("action");
switch (action) {
case "open-faqs":
open_faqs();
break;
case "toggle-gnav":
toggle_gnav(el);
break;
case "sign-in":
signin();
break;
case "open-view":
switch_view(el.dataset.view);
break;
case "close-media":
close_media_preview();
break;
case "close-modal":
close_modal(el);
break;
case "open-profile":
open_profile(el.dataset.pubkey);
break;
case "open-profile-editor":
click_update_profile();
break;
case "show-timeline-new":
show_new();
break;
case "open-thread":
open_trhead(el.dataset.threadId);
break;
case "reply":
send_reply(el.dataset.emoji, el.dataset.to);
break;
case "delete":
delete_post(el.dataset.evid);
break;
case "reply-author":
reply_author(el.dataset.evid);
break;
case "reply-all":
reply_all(el.dataset.evid);
break;
case "react-like":
click_toggle_like(el);
break;
case "share":
click_share(el);
break;
case "open-thread":
open_thread(el.dataset.threadId);
break;
case "open-media":
open_media_preview(el.src, el.dataset.type);
break;
case "show-event-json":
on_click_show_event_details(el.dataset.evid);
break;
case "confirm-delete":
delete_post_confirm(el.dataset.evid);
break;
}
}