csp and http only cookies

This commit is contained in:
Ryan Stafford 2023-07-10 11:53:15 -04:00
parent bf0bc421cd
commit 7ca1e23acf
9 changed files with 109 additions and 68 deletions

View file

@ -167,10 +167,10 @@ summary {
.comment {
font-size: 14px;
margin: 0px 0px 5px 15px;
margin: 0px 0px 5px 0px;
border: 1px solid #e6e6e6;
border-radius: 3px;
padding: 5px 10px 5px 7px;
padding: 10px 10px 0px 7px;
}
.dark .comment {
border-color: #333;
@ -182,7 +182,6 @@ summary {
max-height: 300px;
}
.comment .comment {
margin-left: 15px;
}
.comment .comment,
.comment .comment .comment .comment,
@ -243,18 +242,21 @@ summary {
}
.commentmenu {
font-size: 16px;
margin: 0px 0px 10px 10px;
margin: 0px 0px 10px 0px;
}
.commentmenu div {
border-top: 1px dotted gray;
font-size: 12px;
}
form.savecomment {
margin: 0px 0px 10px 10px;
margin: 0px 0px 10px 0px;
}
.comment > .children > form.savecomment {
margin: 0px 0px 10px 20px;
}
.comment .children {
margin: 5px 0px 10px 15px;
}
.savecomment textarea {
width: 500px;
height: 100px;
@ -275,11 +277,13 @@ form.savecomment {
font-style: italic;
font-weight: 700;
}
.comment.hidden .content, .comment.hidden .children {
.comment.hidden .content, .comment.hidden .children, .comment.hidden .morecomments {
display: none;
}
.children .morecomments {
}
.morecomments {
margin: 10px 0px;
margin: 0px 0px 10px 0px;
font-size: 10px;
}
.morecomments a {
@ -406,7 +410,7 @@ form.nsfw div {
width: 100%;
}
.comment .buttons {
margin: 3px 0px 10px 0px;
margin: 3px 0px 0px 0px;
}
.comment.hidden .buttons {
display: none;
@ -481,7 +485,7 @@ form.nsfw div {
position: relative;
color: #000;
}
#settings {
#settingspopup {
background-color: white;
border: 1px solid #888;
display: none;
@ -489,13 +493,13 @@ form.nsfw div {
right: 10px;
top: 45px;
}
#settings form {
#settingspopup form {
margin: 0px;
}
.dark #settings {
.dark #settingspopup {
background-color: #262626;
}
#settings.open {
#settingspopup.open {
display: inline-block;
}
.expando.open{
@ -585,6 +589,7 @@ form.nsfw div {
}
main {
position: relative;
margin: 0px 10px;
}
@media (min-width: 900px) {
.side {
@ -594,8 +599,8 @@ main {
right: 0;
}
main {
padding-right: 310px;
margin-left: 10px;
padding-right: 316px;
margin-right: 0px;
}
}
.side form {

View file

@ -29,8 +29,8 @@ function postClick(e) {
bdy.className = 'expando open';
btn.className = "expando-button open"
var url = targ.getElementsByClassName("url")[0].href
if (id = parse_youtube(url)) {
targ.getElementsByClassName("embed")[0].innerHTML = youtube_iframe(id)
if (id = parseYoutube(url)) {
targ.getElementsByClassName("embed")[0].innerHTML = youtubeIframe(id)
}
}
}
@ -114,23 +114,26 @@ function loadMore(e) {
request(window.location.origin+window.location.pathname+"?"+urlParams.toString(), "",
function(res){
if (res.trim()) {
e.target.outerHTML = res + '<input id="loadmore" type="submit" data-page="'+(parseInt(page)+1)+'" value="load more" onclick="loadMore(event)">'
e.target.outerHTML = res + '<input id="loadmore" type="submit" data-page="'+(parseInt(page)+1)+'" value="load more">'
if (showimages = document.getElementById("showimages")) {
if (showimages.className == "selected") {
toggle_images(true)
toggleImages(true)
}
}
var loadmore = document.getElementById("loadmore")
if (loadmore) loadmore.className = "show"
insert_youtube()
loadmore.className = "show"
loadmore.addEventListener("click", loadMore)
setup()
}
else {
e.target.outerHTML = '<input id="end" type="submit" value="" disabled>'
}
},
function(res) {
e.target.outerHTML = '<input id="loadmore" type="submit" data-page="'+parseInt(page)+'" value="loading failed" onclick="loadMore(event)">'
document.getElementById("loadmore").className = "show"
e.target.outerHTML = '<input id="loadmore" type="submit" data-page="'+parseInt(page)+'" value="loading failed">'
var loadmore = document.getElementById("loadmore")
loadmore.className = "show"
loadmore.addEventListener("click", loadMore)
}
)
return false;
@ -177,9 +180,9 @@ function formSubmit(e) {
return false
}
function open_settings(e) {
function openSettings(e) {
e.preventDefault()
var settings = document.getElementById("settings")
var settings = document.getElementById("settingspopup")
settings.className = "open"
request(e.target.href + "?xhr=1", "", function(res) {
settings.innerHTML = res
@ -191,22 +194,23 @@ function open_settings(e) {
input[0].checked = "checked"
}
}
document.getElementById("settings").addEventListener("submit", saveSettings)
document.getElementById("closesettings").addEventListener("click", closeSettings)
})
return false
}
function close_settings(e) {
function closeSettings(e) {
e.preventDefault()
var settings = document.getElementById("settings")
var settings = document.getElementById("settingspopup")
settings.className = ""
return false
}
function save_settings(e) {
function saveSettings(e) {
e = e || window.event;
var targ = e.currentTarget || e.srcElement || e;
var data = new FormData(targ)
console.log(data)
e.preventDefault()
var params = new URLSearchParams(data).toString()
request(targ.target, params, function(res) {
@ -218,7 +222,7 @@ function save_settings(e) {
return false;
}
function parse_youtube(url){
function parseYoutube(url){
if (url.indexOf("youtu") == -1) return false
var regExp = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
var match = url.match(regExp);
@ -227,27 +231,26 @@ function parse_youtube(url){
}
return false
}
function youtube_iframe(id) {
function youtubeIframe(id) {
return '<iframe width="560" height="315" src="https://www.youtube.com/embed/'+id+'" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>'
}
function show_images(e) {
function showImages(e) {
e = e || window.event;
e.preventDefault()
var targ = e.currentTarget || e.srcElement || e;
console.log(targ)
var parent = targ.parentNode
if (parent.className == "") {
parent.className = "selected"
toggle_images(true)
toggleImages(true)
} else {
parent.className = ""
toggle_images(false)
toggleImages(false)
}
return false
}
function toggle_images(open) {
function toggleImages(open) {
var posts = document.getElementsByClassName("post")
for (var i = 0; i < posts.length; i++) {
var btn = posts[i].getElementsByClassName("expando-button")[0]
@ -265,27 +268,48 @@ function toggle_images(open) {
}
}
function insert_youtube() {
function setup() {
if (showimages = document.getElementById("se")) {
showimages.addEventListener("click", showImages)
}
if (settings = document.getElementById("opensettings")) {
settings.addEventListener("click", openSettings)
}
if (hidechildren = document.getElementById("hidechildren")){
hidechildren.addEventListener("click", hideAllChildComments)
}
var posts = document.getElementsByClassName("post")
for (var i = 0; i < posts.length; i++) {
posts[i].addEventListener("click", postClick)
var voteForm = posts[i].getElementsByClassName("link-btn")
if (voteForm.length) {
voteForm[0].addEventListener("submit", formSubmit)
}
var url = posts[i].getElementsByClassName("url")[0].href
if (id = parse_youtube(url)) {
if (id = parseYoutube(url)) {
var btn = posts[i].getElementsByClassName("expando-button")[0]
if (btn.className.indexOf("open") > -1) {
posts[i].getElementsByClassName("embed")[0].innerHTML = youtube_iframe(id)
posts[i].getElementsByClassName("embed")[0].innerHTML = youtubeIframe(id)
} else {
btn.className = "expando-button"
}
}
}
var comments = document.getElementsByClassName("comment")
for (var i = 0; i < comments.length; i++) {
comments[i].addEventListener("click", commentClick)
}
}
insert_youtube()
setup()
if (localStorage.getItem("endlessScrolling") == "true") {
var pager = document.getElementsByClassName("pager")
if (pager.length) pager[0].className = "pager hidden"
var loadmore = document.getElementById("loadmore")
if (loadmore) loadmore.className = "show"
if (loadmore) {
loadmore.className = "show"
loadmore.addEventListener("click", loadMore)
}
if (localStorage.getItem("autoLoad") == "true") {
window.onscroll = function(e) {
@ -299,4 +323,11 @@ if (localStorage.getItem("endlessScrolling") == "true") {
}
}
// delete cookies without HTTPOnly
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
var eqPos = cookie.indexOf("=");
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;SameSite=None;Secure";
}

View file

@ -194,7 +194,7 @@ func Initialize(Host string, r *http.Request) (State, error) {
token := getCookie(r, "jwt")
user := getCookie(r, "user")
parts := strings.Split(user, ":")
if len(parts) == 2 {
if len(parts) == 2 && token != "" {
if id, err := strconv.Atoi(parts[1]); err == nil {
state.Client.Token = token
sess := Session{
@ -248,6 +248,8 @@ func Render(w http.ResponseWriter, templateName string, state State) {
if state.Status != http.StatusOK {
w.WriteHeader(state.Status)
}
header := w.Header()
header.Set("Content-Security-Policy", "script-src 'self'")
err = tmpl.Execute(w, state)
if err != nil {
fmt.Println("execute fail", err)
@ -504,10 +506,13 @@ func setCookie(w http.ResponseWriter, host string, name string, value string) {
host = ""
}
cookie := http.Cookie{
Name: name,
Value: value,
MaxAge: 86400 * 30,
Path: "/" + host,
Name: name,
Value: value,
MaxAge: 86400 * 30,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
Secure: true,
Path: "/" + host,
}
http.SetCookie(w, &cookie)
}

View file

@ -1,5 +1,4 @@
<div class="comment{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted }} hidden{{end}}" id="c{{.P.Comment.ID}}" onclick="commentClick(event)">
<div class="meta">
<div class="comment{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted }} hidden{{end}}" id="c{{.P.Comment.ID}}">
{{ if .State.Session }}
<div class="score">
<form class="link-btn{{ if eq .P.MyVote.String "1"}} like{{ else if eq .P.MyVote.String "-1"}} dislike{{end}}" method="POST">
@ -14,6 +13,7 @@
</form>
</div>
{{ end }}
<div class="meta">
<a class="minimize" href="" for="c{{.P.Comment.ID}}">
{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted }}
[+]
@ -105,11 +105,11 @@
</form>
{{ end}}
{{ range $ci, $child := .C }}{{ template "comment.html" $child }}{{end}}
</div>
{{ if ne .P.Counts.ChildCount .ChildCount}}
<div class="morecomments">
<a class="loadmore" for="c{{ .P.Comment.ID}}" href="/{{.State.Host}}/comment/{{.P.Comment.ID}}?">load more comments</a>
<span class="gray">({{ sub .P.Counts.ChildCount .ChildCount}} replies)</span>
</div>
<div class="morecomments">
<a class="loadmore" for="c{{ .P.Comment.ID}}" href="/{{.State.Host}}/comment/{{.P.Comment.ID}}?">load more comments</a>
<span class="gray">({{ sub .P.Counts.ChildCount .ChildCount}} replies)</span>
</div>
{{end}}
</div>
</div>

View file

@ -2,7 +2,7 @@
<head>
<title>{{ if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css?v=16">
<link rel="stylesheet" href="/_/static/style.css?v=17">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body {{ if .Dark }}class="dark"{{end}}>
@ -50,12 +50,12 @@
<div class="pager">
view more: {{if gt .Page 1 }}<a href="{{ .PrevPage }}"> prev</a>{{ end }} <a href="{{ .NextPage }}">next </a>
</div>
<input id="loadmore" type="submit" value="load more" onclick="loadMore(event)" data-page="2">
<input id="loadmore" type="submit" value="load more" data-page="2">
{{ end }}
{{ template "sidebar.html" . }}
</main>
{{ end }}
<script src="/_/static/utils.js?v=9"></script>
<script src="/_/static/utils.js?v=12"></script>
</body>
</html>

View file

@ -3,7 +3,7 @@
<title>{{if and .Posts .PostID }}{{ (index .Posts 0).Post.Name}} : {{.CommunityName}}{{else if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css?v=16">
<link rel="stylesheet" href="/_/static/style.css?v=17">
</head>
<body{{ if .Dark }} class="dark"{{end}}>
<noscript>
@ -122,10 +122,10 @@
<div class="pager">
view more: {{if gt .Page 1 }}<a href="{{ .PrevPage }}"> prev</a>{{ end }} <a href="{{ .NextPage }}">next </a>
</div>
<input id="loadmore" type="submit" value="load more" onclick="loadMore(event)" data-page="2">
<input id="loadmore" type="submit" value="load more" data-page="2">
{{ end }}
<script src="/_/static/utils.js?v=9"></script>
<script src="/_/static/utils.js?v=12"></script>
{{ template "sidebar.html" . }}
</main>
{{ end }}

View file

@ -17,16 +17,16 @@
|
<a href="/{{.Host}}/inbox" class="mailbox{{ if .UnreadCount }} orangered{{end}}"></a>
|
<a href="/{{.Host}}/settings" onclick="open_settings(event)">settings</a>
<a id="opensettings" href="/{{.Host}}/settings">settings</a>
|
<form method="POST"><input type="submit" name="op" value="logout"></form>
{{else}}
<a href="/{{.Host}}/login">log in</a> or <a href="/{{.Host}}/login">sign up</a>
|
<a href="/{{.Host}}/settings" onclick="open_settings(event)">settings</a>
<a id="opensettings" href="/{{.Host}}/settings">settings</a>
{{end}}
</div>
<div id="settings"></div>
<div id="settingspopup"></div>
<div class="spacer">
<a href="/{{ .Host}}/">
<img class="icon" src="{{ if .Site }}{{ .Site.SiteView.Site.Icon.String }}{{else}}/{{ .Host}}/icon.jpg{{end}}">
@ -66,7 +66,7 @@
<li{{ if eq .Sort "NewComments" }} class="selected"{{end}}><a href="{{ .SortBy "NewComments" }}">new comments</a></li>
<li{{ if contains .Sort "Top" }} class="selected"{{end}}><a href="{{ .SortBy "TopDay" }}">top</a></li>
{{ if .Posts }}
<li id="showimages"><a href="" onclick="show_images(event)">show images</a></li>
<li id="showimages"><a id="se" href="">show images</a></li>
{{ end }}
{{ end }}
</ul>

View file

@ -1,12 +1,12 @@
{{ if ne .State.Op "vote_post" }}
<div class="post{{if .Post.Deleted}} deleted{{end}}{{ if or .Post.FeaturedCommunity .Post.FeaturedLocal }} distinguished{{end}}" onclick="postClick(event)">
<div class="post{{if .Post.Deleted}} deleted{{end}}{{ if or .Post.FeaturedCommunity .Post.FeaturedLocal }} distinguished{{end}}">
{{ if gt .Rank 0 }}
<div class="rank"> {{ .Rank }} </div>
{{ end }}
<div class="score">
{{ end }}
{{ if .State.Session }}
<form class="link-btn {{ if lt .Rank 1 }}squish{{end}}{{ if eq .MyVote.String "1" }} like{{else if eq .MyVote.String "-1"}} dislike{{end}}" method="POST" onsubmit="formSubmit(event)">
<form class="link-btn {{ if lt .Rank 1 }}squish{{end}}{{ if eq .MyVote.String "1" }} like{{else if eq .MyVote.String "-1"}} dislike{{end}}" method="POST">
<input type="submit" name="vote" value="▲">
{{ if .MyVote.IsValid}}
<input type="hidden" name="undo" value="{{.MyVote.String}}">
@ -71,7 +71,7 @@
</form>
{{end}}
{{ if .State.PostID }}
<a class="hidechildren" onclick="hideAllChildComments(event)" href="">hide all child comments</a>
<a id="hidechildren" class="scripting" href="">hide all child comments</a>
{{ end }}
</div>
</div>

View file

@ -61,7 +61,7 @@
<div class="error">{{.Error}}</div>
{{ end }}
{{ end }}
<form class="preferences" method="POST" target="/{{.Host}}/settings" onsubmit="save_settings(event)" >
<form id="settings" class="preferences" method="POST" target="/{{.Host}}/settings">
<div>
<label>
default listing
@ -114,7 +114,7 @@
<div>
<label></label>
<input type="submit" value="save">
{{ if .XHR }}<input type="submit" value="close" onclick="close_settings(event)">{{ end }}
{{ if .XHR }}<input id="closesettings" type="submit" value="close">{{ end }}
</div>
</form>
{{ if not .XHR}}