From cb016aac14f04c7caf5d143e140b3c0e4fd7c265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yingyi=20/=20=E9=A2=96=E9=80=B8?= <49649786+Zuoqiu-Yingyi@users.noreply.github.com> Date: Tue, 21 Nov 2023 21:45:44 +0800 Subject: [PATCH] Improve kernel API authentication (#9702) * :art: Add API `/api/network/echo` * :art: Improve localhost checking * :art: Add `model.CheckReadonly` for some APIs /api/storage/setLocalStorage /api/storage/setLocalStorageVal /api/notebook/openNotebook /api/notebook/removeNotebook /api/search/removeTemplate /api/attr/setBlockAttrs /api/sync/importSyncProviderS3 /api/sync/importSyncProviderWebDAV /api/riff/resetRiffCards /api/snippet/setSnippet /api/av/setAttributeViewBlockAttr /api/archive/zip /api/archive/unzip * :art: Remove `model.CheckReadonly` for some APIs /api/history/searchHistory /api/history/getHistoryItems /api/search/findReplace /api/block/getParentNextChildID /api/file/readDir /api/sync/listCloudSyncDir /api/asset/getDocImageAssets /api/template/renderSprig /api/ai/chatGPT /api/ai/chatGPTWithAction * :art: improve API `/api/network/echo` --- app/src/protyle/util/compatibility.ts | 12 ++--- kernel/api/network.go | 65 +++++++++++++++++++++++++++ kernel/api/router.go | 56 ++++++++++++----------- kernel/model/session.go | 4 +- kernel/util/net.go | 24 +++------- 5 files changed, 110 insertions(+), 51 deletions(-) diff --git a/app/src/protyle/util/compatibility.ts b/app/src/protyle/util/compatibility.ts index 290d294d5a8..10be7bfec9e 100644 --- a/app/src/protyle/util/compatibility.ts +++ b/app/src/protyle/util/compatibility.ts @@ -263,11 +263,13 @@ export const getLocalStorage = (cb: () => void) => { }); cb(); - // 数据兼容,移除历史数据,3.8.4 移除 - fetchPost("/api/storage/removeLocalStorageVals", { - app: Constants.SIYUAN_APPID, - keys: ["leftColumn", "local-searchkey", "local-searchedata", "local-searchekeys", "local-searchetabdata", "rightColumn", "topBar"] - }); + if (!window.siyuan.config.readonly) { + // 数据兼容,移除历史数据,3.8.4 移除 + fetchPost("/api/storage/removeLocalStorageVals", { + app: Constants.SIYUAN_APPID, + keys: ["leftColumn", "local-searchkey", "local-searchedata", "local-searchekeys", "local-searchetabdata", "rightColumn", "topBar"] + }); + } }); }; diff --git a/kernel/api/network.go b/kernel/api/network.go index ff00fe5f416..0dbdd77856d 100644 --- a/kernel/api/network.go +++ b/kernel/api/network.go @@ -33,6 +33,71 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func echo(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + password, passwordSet := c.Request.URL.User.Password() + + var rawData any + if data, err := c.GetRawData(); nil == err { + rawData = base64.StdEncoding.EncodeToString(data) + } else { + rawData = nil + } + + ret.Data = map[string]interface{}{ + "Context": map[string]interface{}{ + "Params": c.Params, + "HandlerNames": c.HandlerNames(), + "FullPath": c.FullPath(), + "ClientIP": c.ClientIP(), + "RemoteIP": c.RemoteIP(), + "ContentType": c.ContentType(), + "IsWebsocket": c.IsWebsocket(), + "RawData": rawData, + }, + "Request": map[string]interface{}{ + "Method": c.Request.Method, + "URL": c.Request.URL, + "Proto": c.Request.Proto, + "ProtoMajor": c.Request.ProtoMajor, + "ProtoMinor": c.Request.ProtoMinor, + "Header": c.Request.Header, + "ContentLength": c.Request.ContentLength, + "TransferEncoding": c.Request.TransferEncoding, + "Close": c.Request.Close, + "Host": c.Request.Host, + "Form": c.Request.Form, + "PostForm": c.Request.PostForm, + "MultipartForm": c.Request.MultipartForm, + "Trailer": c.Request.Trailer, + "RemoteAddr": c.Request.RemoteAddr, + "TLS": c.Request.TLS, + "UserAgent": c.Request.UserAgent(), + "Cookies": c.Request.Cookies(), + "Referer": c.Request.Referer(), + }, + "URL": map[string]interface{}{ + "EscapedPath": c.Request.URL.EscapedPath(), + "EscapedFragment": c.Request.URL.EscapedFragment(), + "String": c.Request.URL.String(), + "Redacted": c.Request.URL.Redacted(), + "IsAbs": c.Request.URL.IsAbs(), + "Query": c.Request.URL.Query(), + "RequestURI": c.Request.URL.RequestURI(), + "Hostname": c.Request.URL.Hostname(), + "Port": c.Request.URL.Port(), + }, + "User": map[string]interface{}{ + "Username": c.Request.URL.User.Username(), + "Password": password, + "PasswordSet": passwordSet, + "String": c.Request.URL.User.String(), + }, + } +} + func forwardProxy(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index ee95f32e891..ffb9bc341f4 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -33,6 +33,9 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/system/loginAuth", model.LoginAuth) ginServer.Handle("POST", "/api/system/logoutAuth", model.LogoutAuth) ginServer.Handle("GET", "/api/system/getCaptcha", model.GetCaptcha) + ginServer.Handle("POST", "/api/system/setUILayout", setUILayout) // 这里不加鉴权 After modifying the access authentication code on the browser side, the other side does not refresh https://github.com/siyuan-note/siyuan/issues/8028 + + ginServer.Handle("GET", "/snippets/*filepath", serveSnippets) // 需要鉴权 @@ -56,15 +59,14 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/system/setAppearanceMode", model.CheckAuth, setAppearanceMode) ginServer.Handle("POST", "/api/system/getSysFonts", model.CheckAuth, getSysFonts) ginServer.Handle("POST", "/api/system/exit", model.CheckAuth, exit) - ginServer.Handle("POST", "/api/system/setUILayout", setUILayout) // 这里不加鉴权 After modifying the access authentication code on the browser side, the other side does not refresh https://github.com/siyuan-note/siyuan/issues/8028 ginServer.Handle("POST", "/api/system/getConf", model.CheckAuth, getConf) ginServer.Handle("POST", "/api/system/checkUpdate", model.CheckAuth, checkUpdate) ginServer.Handle("POST", "/api/system/exportLog", model.CheckAuth, exportLog) ginServer.Handle("POST", "/api/system/getChangelog", model.CheckAuth, getChangelog) - ginServer.Handle("POST", "/api/storage/setLocalStorage", model.CheckAuth, setLocalStorage) + ginServer.Handle("POST", "/api/storage/setLocalStorage", model.CheckAuth, model.CheckReadonly, setLocalStorage) ginServer.Handle("POST", "/api/storage/getLocalStorage", model.CheckAuth, getLocalStorage) - ginServer.Handle("POST", "/api/storage/setLocalStorageVal", model.CheckAuth, setLocalStorageVal) + ginServer.Handle("POST", "/api/storage/setLocalStorageVal", model.CheckAuth, model.CheckReadonly, setLocalStorageVal) ginServer.Handle("POST", "/api/storage/removeLocalStorageVals", model.CheckAuth, model.CheckReadonly, removeLocalStorageVals) ginServer.Handle("POST", "/api/storage/setCriterion", model.CheckAuth, model.CheckReadonly, setCriterion) ginServer.Handle("POST", "/api/storage/getCriteria", model.CheckAuth, getCriteria) @@ -78,12 +80,12 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/account/startFreeTrial", model.CheckAuth, model.CheckReadonly, startFreeTrial) ginServer.Handle("POST", "/api/notebook/lsNotebooks", model.CheckAuth, lsNotebooks) - ginServer.Handle("POST", "/api/notebook/openNotebook", model.CheckAuth, openNotebook) + ginServer.Handle("POST", "/api/notebook/openNotebook", model.CheckAuth, model.CheckReadonly, openNotebook) ginServer.Handle("POST", "/api/notebook/closeNotebook", model.CheckAuth, model.CheckReadonly, closeNotebook) ginServer.Handle("POST", "/api/notebook/getNotebookConf", model.CheckAuth, getNotebookConf) ginServer.Handle("POST", "/api/notebook/setNotebookConf", model.CheckAuth, model.CheckReadonly, setNotebookConf) ginServer.Handle("POST", "/api/notebook/createNotebook", model.CheckAuth, model.CheckReadonly, createNotebook) - ginServer.Handle("POST", "/api/notebook/removeNotebook", model.CheckAuth, removeNotebook) + ginServer.Handle("POST", "/api/notebook/removeNotebook", model.CheckAuth, model.CheckReadonly, removeNotebook) ginServer.Handle("POST", "/api/notebook/renameNotebook", model.CheckAuth, model.CheckReadonly, renameNotebook) ginServer.Handle("POST", "/api/notebook/changeSortNotebook", model.CheckAuth, model.CheckReadonly, changeSortNotebook) ginServer.Handle("POST", "/api/notebook/setNotebookIcon", model.CheckAuth, model.CheckReadonly, setNotebookIcon) @@ -122,8 +124,8 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/history/rollbackDocHistory", model.CheckAuth, model.CheckReadonly, rollbackDocHistory) ginServer.Handle("POST", "/api/history/clearWorkspaceHistory", model.CheckAuth, model.CheckReadonly, clearWorkspaceHistory) ginServer.Handle("POST", "/api/history/reindexHistory", model.CheckAuth, model.CheckReadonly, reindexHistory) - ginServer.Handle("POST", "/api/history/searchHistory", model.CheckAuth, model.CheckReadonly, searchHistory) - ginServer.Handle("POST", "/api/history/getHistoryItems", model.CheckAuth, model.CheckReadonly, getHistoryItems) + ginServer.Handle("POST", "/api/history/searchHistory", model.CheckAuth, searchHistory) + ginServer.Handle("POST", "/api/history/getHistoryItems", model.CheckAuth, getHistoryItems) ginServer.Handle("POST", "/api/outline/getDocOutline", model.CheckAuth, getDocOutline) ginServer.Handle("POST", "/api/bookmark/getBookmark", model.CheckAuth, getBookmark) @@ -141,14 +143,14 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/search/searchTag", model.CheckAuth, searchTag) ginServer.Handle("POST", "/api/search/searchTemplate", model.CheckAuth, searchTemplate) - ginServer.Handle("POST", "/api/search/removeTemplate", model.CheckAuth, removeTemplate) + ginServer.Handle("POST", "/api/search/removeTemplate", model.CheckAuth, model.CheckReadonly, removeTemplate) ginServer.Handle("POST", "/api/search/searchWidget", model.CheckAuth, searchWidget) ginServer.Handle("POST", "/api/search/searchRefBlock", model.CheckAuth, searchRefBlock) ginServer.Handle("POST", "/api/search/searchEmbedBlock", model.CheckAuth, searchEmbedBlock) ginServer.Handle("POST", "/api/search/getEmbedBlock", model.CheckAuth, getEmbedBlock) ginServer.Handle("POST", "/api/search/fullTextSearchBlock", model.CheckAuth, fullTextSearchBlock) ginServer.Handle("POST", "/api/search/searchAsset", model.CheckAuth, searchAsset) - ginServer.Handle("POST", "/api/search/findReplace", model.CheckAuth, model.CheckReadonly, findReplace) + ginServer.Handle("POST", "/api/search/findReplace", model.CheckAuth, findReplace) ginServer.Handle("POST", "/api/search/fullTextSearchAssetContent", model.CheckAuth, fullTextSearchAssetContent) ginServer.Handle("POST", "/api/search/getAssetContent", model.CheckAuth, getAssetContent) @@ -183,14 +185,14 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/block/getHeadingChildrenDOM", model.CheckAuth, getHeadingChildrenDOM) ginServer.Handle("POST", "/api/block/swapBlockRef", model.CheckAuth, model.CheckReadonly, swapBlockRef) ginServer.Handle("POST", "/api/block/transferBlockRef", model.CheckAuth, model.CheckReadonly, transferBlockRef) - ginServer.Handle("POST", "/api/block/getParentNextChildID", model.CheckAuth, model.CheckReadonly, getParentNextChildID) + ginServer.Handle("POST", "/api/block/getParentNextChildID", model.CheckAuth, getParentNextChildID) ginServer.Handle("POST", "/api/file/getFile", model.CheckAuth, getFile) ginServer.Handle("POST", "/api/file/putFile", model.CheckAuth, model.CheckReadonly, putFile) ginServer.Handle("POST", "/api/file/copyFile", model.CheckAuth, model.CheckReadonly, copyFile) ginServer.Handle("POST", "/api/file/removeFile", model.CheckAuth, model.CheckReadonly, removeFile) ginServer.Handle("POST", "/api/file/renameFile", model.CheckAuth, model.CheckReadonly, renameFile) - ginServer.Handle("POST", "/api/file/readDir", model.CheckAuth, model.CheckReadonly, readDir) + ginServer.Handle("POST", "/api/file/readDir", model.CheckAuth, readDir) ginServer.Handle("POST", "/api/ref/refreshBacklink", model.CheckAuth, refreshBacklink) ginServer.Handle("POST", "/api/ref/getBacklink", model.CheckAuth, getBacklink) @@ -200,7 +202,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/attr/getBookmarkLabels", model.CheckAuth, getBookmarkLabels) ginServer.Handle("POST", "/api/attr/resetBlockAttrs", model.CheckAuth, model.CheckReadonly, resetBlockAttrs) - ginServer.Handle("POST", "/api/attr/setBlockAttrs", model.CheckAuth, setBlockAttrs) + ginServer.Handle("POST", "/api/attr/setBlockAttrs", model.CheckAuth, model.CheckReadonly, setBlockAttrs) ginServer.Handle("POST", "/api/attr/getBlockAttrs", model.CheckAuth, getBlockAttrs) ginServer.Handle("POST", "/api/cloud/getCloudSpace", model.CheckAuth, getCloudSpace) @@ -215,15 +217,15 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/sync/setCloudSyncDir", model.CheckAuth, model.CheckReadonly, setCloudSyncDir) ginServer.Handle("POST", "/api/sync/createCloudSyncDir", model.CheckAuth, model.CheckReadonly, createCloudSyncDir) ginServer.Handle("POST", "/api/sync/removeCloudSyncDir", model.CheckAuth, model.CheckReadonly, removeCloudSyncDir) - ginServer.Handle("POST", "/api/sync/listCloudSyncDir", model.CheckAuth, model.CheckReadonly, listCloudSyncDir) + ginServer.Handle("POST", "/api/sync/listCloudSyncDir", model.CheckAuth, listCloudSyncDir) ginServer.Handle("POST", "/api/sync/performSync", model.CheckAuth, model.CheckReadonly, performSync) ginServer.Handle("POST", "/api/sync/performBootSync", model.CheckAuth, model.CheckReadonly, performBootSync) ginServer.Handle("POST", "/api/sync/getBootSync", model.CheckAuth, getBootSync) ginServer.Handle("POST", "/api/sync/getSyncInfo", model.CheckAuth, getSyncInfo) ginServer.Handle("POST", "/api/sync/exportSyncProviderS3", model.CheckAuth, exportSyncProviderS3) - ginServer.Handle("POST", "/api/sync/importSyncProviderS3", model.CheckAuth, importSyncProviderS3) + ginServer.Handle("POST", "/api/sync/importSyncProviderS3", model.CheckAuth, model.CheckReadonly, importSyncProviderS3) ginServer.Handle("POST", "/api/sync/exportSyncProviderWebDAV", model.CheckAuth, exportSyncProviderWebDAV) - ginServer.Handle("POST", "/api/sync/importSyncProviderWebDAV", model.CheckAuth, importSyncProviderWebDAV) + ginServer.Handle("POST", "/api/sync/importSyncProviderWebDAV", model.CheckAuth, model.CheckReadonly, importSyncProviderWebDAV) ginServer.Handle("POST", "/api/inbox/getShorthands", model.CheckAuth, getShorthands) ginServer.Handle("POST", "/api/inbox/getShorthand", model.CheckAuth, getShorthand) @@ -243,7 +245,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/asset/getMissingAssets", model.CheckAuth, getMissingAssets) ginServer.Handle("POST", "/api/asset/removeUnusedAsset", model.CheckAuth, model.CheckReadonly, removeUnusedAsset) ginServer.Handle("POST", "/api/asset/removeUnusedAssets", model.CheckAuth, model.CheckReadonly, removeUnusedAssets) - ginServer.Handle("POST", "/api/asset/getDocImageAssets", model.CheckAuth, model.CheckReadonly, getDocImageAssets) + ginServer.Handle("POST", "/api/asset/getDocImageAssets", model.CheckAuth, getDocImageAssets) ginServer.Handle("POST", "/api/asset/renameAsset", model.CheckAuth, model.CheckReadonly, renameAsset) ginServer.Handle("POST", "/api/asset/getImageOCRText", model.CheckAuth, model.CheckReadonly, getImageOCRText) ginServer.Handle("POST", "/api/asset/setImageOCRText", model.CheckAuth, model.CheckReadonly, setImageOCRText) @@ -284,7 +286,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/template/render", model.CheckAuth, renderTemplate) ginServer.Handle("POST", "/api/template/docSaveAsTemplate", model.CheckAuth, model.CheckReadonly, docSaveAsTemplate) - ginServer.Handle("POST", "/api/template/renderSprig", model.CheckAuth, model.CheckReadonly, renderSprig) + ginServer.Handle("POST", "/api/template/renderSprig", model.CheckAuth, renderSprig) ginServer.Handle("POST", "/api/transactions", model.CheckAuth, model.CheckReadonly, performTransactions) @@ -363,33 +365,33 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/riff/getRiffCards", model.CheckAuth, getRiffCards) ginServer.Handle("POST", "/api/riff/getTreeRiffCards", model.CheckAuth, getTreeRiffCards) ginServer.Handle("POST", "/api/riff/getNotebookRiffCards", model.CheckAuth, getNotebookRiffCards) - ginServer.Handle("POST", "/api/riff/resetRiffCards", model.CheckAuth, resetRiffCards) + ginServer.Handle("POST", "/api/riff/resetRiffCards", model.CheckAuth, model.CheckReadonly, resetRiffCards) ginServer.Handle("POST", "/api/notification/pushMsg", model.CheckAuth, pushMsg) ginServer.Handle("POST", "/api/notification/pushErrMsg", model.CheckAuth, pushErrMsg) ginServer.Handle("POST", "/api/snippet/getSnippet", model.CheckAuth, getSnippet) - ginServer.Handle("POST", "/api/snippet/setSnippet", model.CheckAuth, setSnippet) + ginServer.Handle("POST", "/api/snippet/setSnippet", model.CheckAuth, model.CheckReadonly, setSnippet) ginServer.Handle("POST", "/api/snippet/removeSnippet", model.CheckAuth, model.CheckReadonly, removeSnippet) - ginServer.Handle("GET", "/snippets/*filepath", serveSnippets) ginServer.Handle("POST", "/api/av/renderAttributeView", model.CheckAuth, renderAttributeView) ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys) - ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, setAttributeViewBlockAttr) + ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckReadonly, setAttributeViewBlockAttr) - ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, model.CheckReadonly, chatGPT) - ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, model.CheckReadonly, chatGPTWithAction) + ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT) + ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction) ginServer.Handle("POST", "/api/petal/loadPetals", model.CheckAuth, loadPetals) ginServer.Handle("POST", "/api/petal/setPetalEnabled", model.CheckAuth, model.CheckReadonly, setPetalEnabled) - ginServer.Handle("POST", "/api/network/forwardProxy", model.CheckAuth, model.CheckReadonly, forwardProxy) + ginServer.Any("/api/network/echo", model.CheckAuth, echo) + ginServer.Handle("POST", "/api/network/forwardProxy", model.CheckAuth, forwardProxy) ginServer.Handle("GET", "/ws/broadcast", model.CheckAuth, broadcast) - ginServer.Handle("GET", "/api/broadcast/channels", model.CheckAuth, getChannels) ginServer.Handle("POST", "/api/broadcast/postMessage", model.CheckAuth, postMessage) + ginServer.Handle("POST", "/api/broadcast/getChannels", model.CheckAuth, getChannels) ginServer.Handle("POST", "/api/broadcast/getChannelInfo", model.CheckAuth, getChannelInfo) - ginServer.Handle("POST", "/api/archive/zip", model.CheckAuth, zip) - ginServer.Handle("POST", "/api/archive/unzip", model.CheckAuth, unzip) + ginServer.Handle("POST", "/api/archive/zip", model.CheckAuth, model.CheckReadonly, zip) + ginServer.Handle("POST", "/api/archive/unzip", model.CheckAuth, model.CheckReadonly, unzip) } diff --git a/kernel/model/session.go b/kernel/model/session.go index 9cebb8c019a..4243887f596 100644 --- a/kernel/model/session.go +++ b/kernel/model/session.go @@ -163,10 +163,12 @@ func CheckAuth(c *gin.Context) { // 未设置访问授权码 if "" == Conf.AccessAuthCode { // Authenticate requests with the Origin header other than 127.0.0.1 https://github.com/siyuan-note/siyuan/issues/9180 + clientIP := c.ClientIP() host := c.GetHeader("Host") origin := c.GetHeader("Origin") forwardedHost := c.GetHeader("X-Forwarded-Host") if !localhost || + ("" != clientIP && !util.IsLocalHostname(clientIP)) || ("" != host && !util.IsLocalHost(host)) || ("" != origin && !util.IsLocalOrigin(origin) && !strings.HasPrefix(origin, "chrome-extension://")) || ("" != forwardedHost && !util.IsLocalHost(forwardedHost)) { @@ -243,7 +245,7 @@ func CheckAuth(c *gin.Context) { if workspaceSession.AccessAuthCode != Conf.AccessAuthCode { userAgentHeader := c.GetHeader("User-Agent") if strings.HasPrefix(userAgentHeader, "SiYuan/") || strings.HasPrefix(userAgentHeader, "Mozilla/") { - if "GET" != c.Request.Method { + if "GET" != c.Request.Method || c.IsWebsocket() { c.JSON(http.StatusUnauthorized, map[string]interface{}{"code": -1, "msg": Conf.Language(156)}) c.Abort() return diff --git a/kernel/util/net.go b/kernel/util/net.go index f97a8b9a7c8..bace9a366f6 100644 --- a/kernel/util/net.go +++ b/kernel/util/net.go @@ -47,23 +47,8 @@ func ValidOptionalPort(port string) bool { return true } -func SplitHost(host string) (hostname, port string) { - hostname = host - - colon := strings.LastIndexByte(hostname, ':') - if colon != -1 && ValidOptionalPort(hostname[colon:]) { - hostname, port = hostname[:colon], hostname[colon+1:] - } - - if strings.HasPrefix(hostname, "[") && strings.HasSuffix(hostname, "]") { - hostname = hostname[1 : len(hostname)-1] - } - - return -} - func IsLocalHostname(hostname string) bool { - if "localhost" == hostname { + if "localhost" == hostname || strings.HasSuffix(hostname, ".localhost") { return true } if ip := net.ParseIP(hostname); nil != ip { @@ -73,8 +58,11 @@ func IsLocalHostname(hostname string) bool { } func IsLocalHost(host string) bool { - hostname, _ := SplitHost(host) - return IsLocalHostname(hostname) + if hostname, _, err := net.SplitHostPort(strings.TrimSpace(host)); nil != err { + return false + } else { + return IsLocalHostname(hostname) + } } func IsLocalOrigin(origin string) bool {