aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2020-02-25 16:13:10 +0100
committerSimon Ser <contact@emersion.fr>2020-02-25 16:13:10 +0100
commitb3f98de1da11e13dea5c08af4f80520258c120cf (patch)
treeb3a4443b307b222fce6c0d40daf4d2032c14002c
parenta8a3c82579ac244dde56320224f6bd15f17427f7 (diff)
downloadalps-b3f98de1da11e13dea5c08af4f80520258c120cf.tar.gz
alps-b3f98de1da11e13dea5c08af4f80520258c120cf.zip
plugins/viewhtml: add opt-in query param to load remote resources
Loading remote resources by default has privacy implications.
-rw-r--r--plugins/viewhtml/plugin.go12
-rw-r--r--plugins/viewhtml/sanitize.go8
-rw-r--r--plugins/viewhtml/viewer.go9
-rw-r--r--themes/sourcehut/message.html7
4 files changed, 33 insertions, 3 deletions
diff --git a/plugins/viewhtml/plugin.go b/plugins/viewhtml/plugin.go
index b34372f..1007d4a 100644
--- a/plugins/viewhtml/plugin.go
+++ b/plugins/viewhtml/plugin.go
@@ -9,6 +9,7 @@ import (
"strings"
"git.sr.ht/~emersion/koushin"
+ koushinbase "git.sr.ht/~emersion/koushin/plugins/base"
"github.com/labstack/echo/v4"
)
@@ -20,6 +21,17 @@ var (
func init() {
p := koushin.GoPlugin{Name: "viewhtml"}
+ p.Inject("message.html", func(ctx *koushin.Context, _data koushin.RenderData) error {
+ data := _data.(*koushinbase.MessageRenderData)
+ data.Extra["RemoteResourcesAllowed"] = ctx.QueryParam("allow-remote-resources") == "1"
+ hasRemoteResources := false
+ if v := ctx.Get("viewhtml.hasRemoteResources"); v != nil {
+ hasRemoteResources = v.(bool)
+ }
+ data.Extra["HasRemoteResources"] = hasRemoteResources
+ return nil
+ })
+
p.GET("/proxy", func(ctx *koushin.Context) error {
if !proxyEnabled {
return echo.NewHTTPError(http.StatusForbidden, "proxy disabled")
diff --git a/plugins/viewhtml/sanitize.go b/plugins/viewhtml/sanitize.go
index a931fe1..ba2aca9 100644
--- a/plugins/viewhtml/sanitize.go
+++ b/plugins/viewhtml/sanitize.go
@@ -71,7 +71,9 @@ var allowedStyles = map[string]bool{
}
type sanitizer struct {
- msg *koushinbase.IMAPMessage
+ msg *koushinbase.IMAPMessage
+ allowRemoteResources bool
+ hasRemoteResources bool
}
func (san *sanitizer) sanitizeImageURL(src string) string {
@@ -94,7 +96,9 @@ func (san *sanitizer) sanitizeImageURL(src string) string {
return part.URL(true).String()
case "https":
- if !proxyEnabled {
+ san.hasRemoteResources = true
+
+ if !proxyEnabled || !san.allowRemoteResources {
return "about:blank"
}
diff --git a/plugins/viewhtml/viewer.go b/plugins/viewhtml/viewer.go
index 47f5eea..abc9f2d 100644
--- a/plugins/viewhtml/viewer.go
+++ b/plugins/viewhtml/viewer.go
@@ -25,6 +25,8 @@ var tpl = template.Must(template.New("view-html.html").Parse(tplSrc))
type viewer struct{}
func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage, part *message.Entity) (interface{}, error) {
+ allowRemoteResources := ctx.QueryParam("allow-remote-resources") == "1"
+
mimeType, _, err := part.Header.ContentType()
if err != nil {
return nil, err
@@ -38,12 +40,17 @@ func (viewer) ViewMessagePart(ctx *koushin.Context, msg *koushinbase.IMAPMessage
return nil, fmt.Errorf("failed to read part body: %v", err)
}
- san := sanitizer{msg}
+ san := sanitizer{
+ msg: msg,
+ allowRemoteResources: allowRemoteResources,
+ }
body, err = san.sanitizeHTML(body)
if err != nil {
return nil, fmt.Errorf("failed to sanitize HTML part: %v", err)
}
+ ctx.Set("viewhtml.hasRemoteResources", san.hasRemoteResources)
+
var buf bytes.Buffer
err = tpl.Execute(&buf, string(body))
if err != nil {
diff --git a/themes/sourcehut/message.html b/themes/sourcehut/message.html
index fb38932..17f5976 100644
--- a/themes/sourcehut/message.html
+++ b/themes/sourcehut/message.html
@@ -99,6 +99,13 @@
{{end}}
</ul>
+ {{if and .Extra.HasRemoteResources (not .Extra.RemoteResourcesAllowed)}}
+ <p class="alert alert-info">
+ This message contains remote content.
+ <a href="?part={{.PartPath}}&allow-remote-resources=1" class="alert-link">Load</a>
+ </p>
+ {{end}}
+
{{if .View}}
{{.View}}
{{else}}