From b624fea2556310ca686697ff5c2a64892b00842b Mon Sep 17 00:00:00 2001 From: gsw945 Date: Sat, 9 Oct 2021 18:22:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0iframe=E6=94=AF=E6=8C=81(conf?= =?UTF-8?q?=20`enable=5Fiframe`)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commands/command.go | 6 ++ conf/app.conf.example | 1 + conf/enumerate.go | 14 +++ routers/router.go | 71 +++++++++++++++ static/js/blog.js | 8 +- .../js/custom-elements-builtin-0.6.5.min.js | 1 + static/js/markdown.js | 11 ++- static/js/x-frame-bypass-1.0.2.js | 87 +++++++++++++++++++ utils/html.go | 20 +++-- views/document/default_read.tpl | 6 ++ views/document/document_password.tpl | 6 ++ views/document/html_edit_template.tpl | 7 +- views/document/kancloud_read_template.tpl | 6 ++ views/document/markdown_edit_template.tpl | 6 ++ views/document/new_html_edit_template.tpl | 6 ++ 15 files changed, 240 insertions(+), 16 deletions(-) create mode 100644 static/js/custom-elements-builtin-0.6.5.min.js create mode 100644 static/js/x-frame-bypass-1.0.2.js diff --git a/commands/command.go b/commands/command.go index 2cc8ea95..a3a42e55 100644 --- a/commands/command.go +++ b/commands/command.go @@ -263,6 +263,12 @@ func RegisterFunction() { logs.Error("注册函数 urlfor 出错 ->", err) os.Exit(-1) } + //读取配置值(未作任何转换) + err = web.AddFuncMap("conf", conf.CONF) + if err != nil { + logs.Error("注册函数 conf 出错 ->", err) + os.Exit(-1) + } err = web.AddFuncMap("date_format", func(t time.Time, format string) string { return t.Local().Format(format) }) diff --git a/conf/app.conf.example b/conf/app.conf.example index 91872dbe..6dccf6f1 100644 --- a/conf/app.conf.example +++ b/conf/app.conf.example @@ -8,6 +8,7 @@ sessionon = true sessionname = mindoc_id copyrequestbody = true enablexsrf = "${MINDOC_ENABLE_XSRF||false}" +enable_iframe = "${MINDOC_ENABLE_IFRAME||false}" #系统完整URL(http://doc.iminho.me),如果该项不设置,会从请求头中获取地址。 baseurl="${MINDOC_BASE_URL}" diff --git a/conf/enumerate.go b/conf/enumerate.go index d799ca4f..3d282179 100644 --- a/conf/enumerate.go +++ b/conf/enumerate.go @@ -152,6 +152,11 @@ func GetEnableExport() bool { return web.AppConfig.DefaultBool("enable_export", true) } +//是否启用iframe +func GetEnableIframe() bool { + return web.AppConfig.DefaultBool("enable_iframe", false) +} + //同一项目导出线程的并发数 func GetExportProcessNum() int { exportProcessNum := web.AppConfig.DefaultInt("export_process_num", 1) @@ -208,6 +213,15 @@ func IsAllowUploadFileExt(ext string) bool { return false } +//读取配置文件值 +func CONF(key string, value ...string) string { + defaultValue := "" + if len(value) > 0 { + defaultValue = value[0] + } + return web.AppConfig.DefaultString(key, defaultValue) +} + //重写生成URL的方法,加上完整的域名 func URLFor(endpoint string, values ...interface{}) string { baseUrl := web.AppConfig.DefaultString("baseurl", "") diff --git a/routers/router.go b/routers/router.go index 0335e62e..d2eaf880 100644 --- a/routers/router.go +++ b/routers/router.go @@ -1,11 +1,82 @@ package routers import ( + "crypto/tls" + "log" + "net/http" + "net/http/httputil" + "net/url" + "strings" + + "github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/server/web" + "github.com/beego/beego/v2/server/web/context" + "github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/controllers" ) +func rt(req *http.Request) (*http.Response, error) { + log.Printf("request received. url=%s", req.URL) + // req.Header.Set("Host", "httpbin.org") // <--- I set it here as well + defer log.Printf("request complete. url=%s", req.URL) + + return http.DefaultTransport.RoundTrip(req) +} + +// roundTripper makes func signature a http.RoundTripper +type roundTripper func(*http.Request) (*http.Response, error) + +func (f roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } + +type CorsTransport struct { + http.RoundTripper +} + +func (t *CorsTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + // refer: + // - https://stackoverflow.com/questions/31535569/golang-how-to-read-response-body-of-reverseproxy/31536962#31536962 + // - https://gist.github.com/simon-cj/b4da0b2bca793ec3b8a5abe04c8fca41 + resp, err = t.RoundTripper.RoundTrip(req) + logs.Debug(resp) + if err != nil { + return nil, err + } + resp.Header.Del("Access-Control-Request-Method") + resp.Header.Set("Access-Control-Allow-Origin", "*") + return resp, nil +} + func init() { + web.Any("/cors-anywhere", func(ctx *context.Context) { + u, _ := url.PathUnescape(ctx.Input.Query("url")) + logs.Error("ReverseProxy: ", u) + if len(u) > 0 && strings.HasPrefix(u, "http") { + if strings.TrimRight(conf.BaseUrl, "/") == ctx.Input.Site() { + ctx.Redirect(302, u) + } else { + target, _ := url.Parse(u) + logs.Debug("target: ", target) + + proxy := &httputil.ReverseProxy{ + Transport: roundTripper(rt), + Director: func(req *http.Request) { + req.Header = ctx.Request.Header + req.URL.Scheme = target.Scheme + req.URL.Host = target.Host + req.URL.Path = target.Path + req.Header.Set("Host", target.Host) + }, + } + + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + proxy.ServeHTTP(ctx.ResponseWriter, ctx.Request) + } + } else { + ctx.ResponseWriter.WriteHeader(http.StatusBadRequest) + ctx.Output.Body([]byte("400 Bad Request")) + } + }) + web.Router("/", &controllers.HomeController{}, "*:Index") web.Router("/login", &controllers.AccountController{}, "*:Login") diff --git a/static/js/blog.js b/static/js/blog.js index 6a19c4f6..594806ea 100644 --- a/static/js/blog.js +++ b/static/js/blog.js @@ -3,6 +3,10 @@ $(function () { js : window.katex.js, css : window.katex.css }; + var htmlDecodeList = ["style","script","title","onmouseover","onmouseout","style"]; + if (!window.IS_ENABLE_IFRAME) { + htmlDecodeList.unshift("iframe"); + } window.editor = editormd("docEditor", { width: "100%", height: "100%", @@ -18,8 +22,8 @@ $(function () { taskList: true, flowChart: true, mermaid: true, - htmlDecode: "style,script,iframe,title,onmouseover,onmouseout,style", - lineNumbers: false, + htmlDecode: htmlDecodeList.join(','), + lineNumbers: true, sequenceDiagram: true, highlightStyle: window.highlightStyle ? window.highlightStyle : "github", tocStartLevel: 1, diff --git a/static/js/custom-elements-builtin-0.6.5.min.js b/static/js/custom-elements-builtin-0.6.5.min.js new file mode 100644 index 00000000..7f176025 --- /dev/null +++ b/static/js/custom-elements-builtin-0.6.5.min.js @@ -0,0 +1 @@ +!function(P,H,k){"use strict";if(1==P.importNode.length&&!H.get("ungap-li")){var D="extends";try{var e={extends:"li"},t=HTMLLIElement,n=function(){return Reflect.construct(t,[],n)};if(n.prototype=k.create(t.prototype),H.define("ungap-li",n,e),!/is="ungap-li"/.test((new n).outerHTML))throw e}catch(e){!function(){var l="attributeChangedCallback",n="connectedCallback",r="disconnectedCallback",e=Element.prototype,i=k.assign,t=k.create,o=k.defineProperties,a=k.getOwnPropertyDescriptor,s=k.setPrototypeOf,u=H.define,c=H.get,f=H.upgrade,p=H.whenDefined,v=t(null),d=new WeakMap,g={childList:!0,subtree:!0};Reflect.ownKeys(self).filter(function(e){return"string"==typeof e&&/^HTML(?!Element)/.test(e)}).forEach(function(e){function t(){}var n=self[e];s(t,n),(t.prototype=n.prototype).constructor=t,(n={})[e]={value:t},o(self,n)}),new MutationObserver(m).observe(P,g),O(Document.prototype,"importNode"),O(Node.prototype,"cloneNode"),o(H,{define:{value:function(e,t,n){if(e=e.toLowerCase(),n&&D in n){v[e]=i({},n,{Class:t});for(var e=n[D]+'[is="'+e+'"]',r=P.querySelectorAll(e),o=0,a=r.length;o + + + + +
+ +` + this.fetchProxy(url, options, 0).then(res => res.text()).then(data => { + if (data) + this.srcdoc = data.replace(/]*)>/i, ` + + `) + }).catch(e => console.error('Cannot load X-Frame-Bypass:', e)) + } + fetchProxy (url, options, i) { + const proxies = (options || {}).proxies || [ + window.BASE_URL + 'cors-anywhere?url=', + 'https://cors-anywhere.herokuapp.com/', + 'https://yacdn.org/proxy/', + 'https://api.codetabs.com/v1/proxy/?quest=' + ] + return fetch(proxies[i] + url, options).then(res => { + if (!res.ok) + throw new Error(`${res.status} ${res.statusText}`); + return res + }).catch(error => { + if (i === proxies.length - 1) + throw error + return this.fetchProxy(url, options, i + 1) + }) + } +}, {extends: 'iframe'}) \ No newline at end of file diff --git a/utils/html.go b/utils/html.go index c9bb137b..dd13daf5 100644 --- a/utils/html.go +++ b/utils/html.go @@ -9,7 +9,7 @@ import ( "github.com/mindoc-org/mindoc/conf" ) -func StripTags(s string) string { +func StripTags(s string) string { //将HTML标签全转换成小写 re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") @@ -33,8 +33,9 @@ func StripTags(s string) string { return src } + //自动提取文章摘要 -func AutoSummary(body string,l int) string { +func AutoSummary(body string, l int) string { //匹配图片,如果图片语法是在代码块中,这里同样会处理 re := regexp.MustCompile(`

(.*?)

`) @@ -42,11 +43,11 @@ func AutoSummary(body string,l int) string { contents := re.FindAllString(body, -1) if len(contents) <= 0 { - return "" + return "" } content := "" - for _,s := range contents { - b := strings.Replace(StripTags(s),"\n","", -1) + for _, s := range contents { + b := strings.Replace(StripTags(s), "\n", "", -1) if l <= 0 { break @@ -70,7 +71,9 @@ func SafetyProcessor(html string) string { docQuery.Find("applet").Remove() docQuery.Find("frame").Remove() docQuery.Find("meta").Remove() - docQuery.Find("iframe").Remove() + if !conf.GetEnableIframe() { + docQuery.Find("iframe").Remove() + } docQuery.Find("*").Each(func(i int, selection *goquery.Selection) { if href, ok := selection.Attr("href"); ok && strings.HasPrefix(href, "javascript:") { @@ -117,10 +120,9 @@ func SafetyProcessor(html string) string { } } - if html, err := docQuery.Html(); err == nil { - return strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(html), ""), "") + return strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(html), ""), "") } } return html -} \ No newline at end of file +} diff --git a/views/document/default_read.tpl b/views/document/default_read.tpl index f11a9f76..86c9a5fc 100644 --- a/views/document/default_read.tpl +++ b/views/document/default_read.tpl @@ -29,6 +29,10 @@ + +
@@ -109,6 +113,8 @@
+ + + + +
@@ -173,6 +177,8 @@ + + + + + +