mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-27 02:36:10 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
b2f8807dc0
18 changed files with 217 additions and 20 deletions
|
|
@ -64,3 +64,5 @@ extraResources:
|
|||
filter: "!**/{.DS_Store}"
|
||||
- from: "pandoc/pandoc-darwin-arm64.zip"
|
||||
to: "pandoc.zip"
|
||||
- from: "pandoc/pandoc-template.docx"
|
||||
to: "pandoc-template.docx"
|
||||
|
|
|
|||
|
|
@ -64,3 +64,5 @@ extraResources:
|
|||
filter: "!**/{.DS_Store}"
|
||||
- from: "pandoc/pandoc-darwin-amd64.zip"
|
||||
to: "pandoc.zip"
|
||||
- from: "pandoc/pandoc-template.docx"
|
||||
to: "pandoc-template.docx"
|
||||
|
|
|
|||
|
|
@ -67,4 +67,6 @@ extraResources:
|
|||
to: "appearance/fonts"
|
||||
filter: "!**/{.DS_Store}"
|
||||
- from: "pandoc/pandoc-linux-arm64.zip"
|
||||
to: "pandoc.zip"
|
||||
to: "pandoc.zip"
|
||||
- from: "pandoc/pandoc-template.docx"
|
||||
to: "pandoc-template.docx"
|
||||
|
|
|
|||
|
|
@ -64,4 +64,6 @@ extraResources:
|
|||
to: "appearance/fonts"
|
||||
filter: "!**/{.DS_Store}"
|
||||
- from: "pandoc/pandoc-linux-amd64.zip"
|
||||
to: "pandoc.zip"
|
||||
to: "pandoc.zip"
|
||||
- from: "pandoc/pandoc-template.docx"
|
||||
to: "pandoc-template.docx"
|
||||
|
|
|
|||
|
|
@ -71,3 +71,5 @@ extraResources:
|
|||
filter: "!**/{.DS_Store}"
|
||||
- from: "pandoc/pandoc-windows-amd64.zip"
|
||||
to: "pandoc.zip"
|
||||
- from: "pandoc/pandoc-template.docx"
|
||||
to: "pandoc-template.docx"
|
||||
|
|
|
|||
BIN
app/pandoc/pandoc-template.docx
Normal file
BIN
app/pandoc/pandoc-template.docx
Normal file
Binary file not shown.
|
|
@ -27,8 +27,10 @@ export const unicode2Emoji = (unicode: string, className = "", needSpan = false,
|
|||
let emoji = "";
|
||||
if (unicode.startsWith("api/icon/getDynamicIcon")) {
|
||||
emoji = `<img class="${className}" ${lazy ? "data-" : ""}src="${unicode}"/>`;
|
||||
emoji = Lute.Sanitize(emoji);
|
||||
} else if (unicode.indexOf(".") > -1) {
|
||||
emoji = `<img class="${className}" ${lazy ? "data-" : ""}src="/emojis/${unicode}"/>`;
|
||||
emoji = Lute.Sanitize(emoji);
|
||||
} else {
|
||||
try {
|
||||
unicode.split("-").forEach(item => {
|
||||
|
|
|
|||
|
|
@ -104,8 +104,12 @@ export class Title {
|
|||
event.stopPropagation();
|
||||
let textPlain = await readText() || "";
|
||||
if (textPlain) {
|
||||
// 对 <<assets/...>> 进行内部转义 https://github.com/siyuan-note/siyuan/issues/11992
|
||||
textPlain = textPlain.replace(/<<assets\//g, "__@lt2assets/@__").replace(/>>/g, "__@gt2@__");
|
||||
// 对 HTML 标签进行内部转义,避免被 Lute 解析以后变为小写 https://github.com/siyuan-note/siyuan/issues/10620
|
||||
textPlain = textPlain.replace(/</g, ";;;lt;;;").replace(/>/g, ";;;gt;;;");
|
||||
// 反转义 <<assets/...>>
|
||||
textPlain = textPlain.replace(/__@lt2assets\/@__/g, "<<assets/").replace(/__@gt2@__/g, ">>");
|
||||
enableLuteMarkdownSyntax(protyle);
|
||||
let content = protyle.lute.BlockDOM2EscapeMarkerContent(protyle.lute.Md2BlockDOM(textPlain));
|
||||
restoreLuteMarkdownSyntax(protyle);
|
||||
|
|
@ -273,7 +277,9 @@ export class Title {
|
|||
accelerator: "⇧⌘V",
|
||||
click: async () => {
|
||||
let textPlain = await readText() || "";
|
||||
textPlain = textPlain.replace(/<<assets\//g, "__@lt2assets/@__").replace(/>>/g, "__@gt2@__");
|
||||
textPlain = textPlain.replace(/</g, ";;;lt;;;").replace(/>/g, ";;;gt;;;");
|
||||
textPlain = textPlain.replace(/__@lt2assets\/@__/g, "<<assets/").replace(/__@gt2@__/g, ">>");
|
||||
enableLuteMarkdownSyntax(protyle);
|
||||
let content = protyle.lute.BlockDOM2EscapeMarkerContent(protyle.lute.Md2BlockDOM(textPlain));
|
||||
restoreLuteMarkdownSyntax(protyle);
|
||||
|
|
|
|||
|
|
@ -175,9 +175,15 @@ export const pasteAsPlainText = async (protyle: IProtyle) => {
|
|||
// 删掉 <span data-type\="text".*>text</span> 标签,只保留文本
|
||||
textPlain = textPlain.replace(/<span data-type="text".*?>(.*?)<\/span>/g, "$1");
|
||||
|
||||
// 对 <<assets/...>> 进行内部转义 https://github.com/siyuan-note/siyuan/issues/11992
|
||||
textPlain = textPlain.replace(/<<assets\//g, "__@lt2assets/@__").replace(/>>/g, "__@gt2@__");
|
||||
|
||||
// 对 HTML 标签进行内部转义,避免被 Lute 解析以后变为小写 https://github.com/siyuan-note/siyuan/issues/10620
|
||||
textPlain = textPlain.replace(/</g, ";;;lt;;;").replace(/>/g, ";;;gt;;;");
|
||||
|
||||
// 反转义 <<assets/...>>
|
||||
textPlain = textPlain.replace(/__@lt2assets\/@__/g, "<<assets/").replace(/__@gt2@__/g, ">>");
|
||||
|
||||
// 反转义内置需要解析的 HTML 标签
|
||||
textPlain = textPlain.replace(/__@sub@__/g, "<sub>").replace(/__@\/sub@__/g, "</sub>");
|
||||
textPlain = textPlain.replace(/__@sup@__/g, "<sup>").replace(/__@\/sup@__/g, "</sup>");
|
||||
|
|
|
|||
|
|
@ -66,14 +66,26 @@ func globalCopyFiles(c *gin.Context) {
|
|||
srcs = append(srcs, s.(string))
|
||||
}
|
||||
|
||||
for _, src := range srcs {
|
||||
if !filelock.IsExist(src) {
|
||||
for i, src := range srcs {
|
||||
absSrc, _ := filepath.Abs(src)
|
||||
|
||||
if !filelock.IsExist(absSrc) {
|
||||
msg := fmt.Sprintf("file [%s] does not exist", src)
|
||||
logging.LogErrorf(msg)
|
||||
ret.Code = -1
|
||||
ret.Msg = msg
|
||||
return
|
||||
}
|
||||
|
||||
if util.IsSensitivePath(absSrc) {
|
||||
msg := fmt.Sprintf("refuse to copy sensitive file [%s]", src)
|
||||
logging.LogErrorf(msg)
|
||||
ret.Code = -2
|
||||
ret.Msg = msg
|
||||
return
|
||||
}
|
||||
|
||||
srcs[i] = absSrc
|
||||
}
|
||||
|
||||
destDir := arg["destDir"].(string) // 相对于工作空间的路径
|
||||
|
|
@ -155,6 +167,13 @@ func getFile(c *gin.Context) {
|
|||
c.JSON(http.StatusAccepted, ret)
|
||||
return
|
||||
}
|
||||
if !filelock.IsExist(fileAbsPath) {
|
||||
ret.Code = http.StatusNotFound
|
||||
ret.Msg = "file does not exist"
|
||||
c.JSON(http.StatusAccepted, ret)
|
||||
return
|
||||
}
|
||||
|
||||
info, err := os.Stat(fileAbsPath)
|
||||
if os.IsNotExist(err) {
|
||||
ret.Code = http.StatusNotFound
|
||||
|
|
@ -178,19 +197,8 @@ func getFile(c *gin.Context) {
|
|||
}
|
||||
|
||||
// REF: https://github.com/siyuan-note/siyuan/issues/11364
|
||||
if role := model.GetGinContextRole(c); !model.IsValidRole(role, []model.Role{
|
||||
model.RoleAdministrator,
|
||||
}) {
|
||||
if relPath, err := filepath.Rel(util.ConfDir, fileAbsPath); err != nil {
|
||||
logging.LogErrorf("Get a relative path from [%s] to [%s] failed: %s", util.ConfDir, fileAbsPath, err)
|
||||
ret.Code = http.StatusInternalServerError
|
||||
ret.Msg = err.Error()
|
||||
c.JSON(http.StatusAccepted, ret)
|
||||
return
|
||||
} else if relPath == "conf.json" {
|
||||
ret.Code = http.StatusForbidden
|
||||
ret.Msg = http.StatusText(http.StatusForbidden)
|
||||
c.JSON(http.StatusAccepted, ret)
|
||||
if !model.IsAdminRoleContext(c) {
|
||||
if refuseToAccess(c, fileAbsPath, ret) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -216,6 +224,33 @@ func getFile(c *gin.Context) {
|
|||
c.Data(http.StatusOK, contentType, data)
|
||||
}
|
||||
|
||||
func refuseToAccess(c *gin.Context, fileAbsPath string, ret *gulu.Result) bool {
|
||||
// 禁止访问配置文件 conf/conf.json
|
||||
if filepath.Join(util.ConfDir, "conf.json") == fileAbsPath {
|
||||
ret.Code = http.StatusForbidden
|
||||
ret.Msg = http.StatusText(http.StatusForbidden)
|
||||
c.JSON(http.StatusAccepted, ret)
|
||||
return true
|
||||
}
|
||||
|
||||
// 禁止访问 data/snippets/conf.json
|
||||
if filepath.Join(util.DataDir, "snippets", "conf.json") == fileAbsPath {
|
||||
ret.Code = http.StatusForbidden
|
||||
ret.Msg = http.StatusText(http.StatusForbidden)
|
||||
c.JSON(http.StatusAccepted, ret)
|
||||
return true
|
||||
}
|
||||
|
||||
// 禁止访问 data/templates 目录
|
||||
if util.IsSubPath(filepath.Join(util.DataDir, "templates"), fileAbsPath) {
|
||||
ret.Code = http.StatusForbidden
|
||||
ret.Msg = http.StatusText(http.StatusForbidden)
|
||||
c.JSON(http.StatusAccepted, ret)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readDir(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
|
|
|||
|
|
@ -164,6 +164,10 @@ func getDynamicIcon(c *gin.Context) {
|
|||
svg = generateTypeOneSVG(color, lang, dateInfo)
|
||||
}
|
||||
|
||||
if !model.Conf.Editor.AllowSVGScript {
|
||||
svg = util.RemoveScriptsInSVG(svg)
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "image/svg+xml")
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
c.Header("Pragma", "no-cache")
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ require (
|
|||
github.com/jaypipes/ghw v0.21.2
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klippa-app/go-pdfium v1.17.2
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
|
|
|
|||
|
|
@ -262,6 +262,8 @@ github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
|
|||
github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
|
|
|
|||
|
|
@ -248,7 +248,18 @@ func netAssets2LocalAssets0(tree *parse.Tree, onlyImg bool, originalURL string,
|
|||
u = u[:strings.Index(u, "?")]
|
||||
}
|
||||
|
||||
if !gulu.File.IsExist(u) || gulu.File.IsDir(u) {
|
||||
if !gulu.File.IsExist(u) {
|
||||
logging.LogErrorf("local file asset [%s] not exist", u)
|
||||
continue
|
||||
}
|
||||
|
||||
if gulu.File.IsDir(u) {
|
||||
logging.LogWarnf("ignore converting directory path [%s] to local asset", u)
|
||||
continue
|
||||
}
|
||||
|
||||
if util.IsSensitivePath(u) {
|
||||
logging.LogWarnf("ignore converting sensitive path [%s] to local asset", u)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1262,6 +1262,14 @@ func subscribeConfEvents() {
|
|||
eventbus.Subscribe(util.EvtConfPandocInitialized, func() {
|
||||
logging.LogInfof("pandoc initialized, set pandoc bin to [%s]", util.PandocBinPath)
|
||||
Conf.Export.PandocBin = util.PandocBinPath
|
||||
|
||||
params := util.RemoveInvalid(Conf.Export.PandocParams)
|
||||
if !strings.Contains(params, "--reference-doc") && "" != util.PandocTemplatePath {
|
||||
params += " --reference-doc"
|
||||
params += " \"" + util.PandocTemplatePath + "\""
|
||||
Conf.Export.PandocParams = strings.TrimSpace(params)
|
||||
}
|
||||
|
||||
Conf.Save()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import (
|
|||
"github.com/emirpasic/gods/sets/hashset"
|
||||
"github.com/emirpasic/gods/stacks/linkedliststack"
|
||||
"github.com/imroc/req/v3"
|
||||
shellquote "github.com/kballard/go-shellquote"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/api"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/font"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
|
||||
|
|
@ -773,7 +774,12 @@ func ExportDocx(id, savePath string, removeAssets, merge bool) (fullPath string,
|
|||
|
||||
params := util.RemoveInvalid(Conf.Export.PandocParams)
|
||||
if "" != params {
|
||||
args = append(args, strings.Split(params, " ")...)
|
||||
customArgs, parseErr := shellquote.Split(params)
|
||||
if nil != parseErr {
|
||||
logging.LogErrorf("parse pandoc custom params [%s] failed: %s", params, parseErr)
|
||||
} else {
|
||||
args = append(args, customArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
pandoc := exec.Command(Conf.Export.PandocBin, args...)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,8 @@ func Pandoc(from, to, o, content string) (err error) {
|
|||
}
|
||||
|
||||
var (
|
||||
PandocBinPath string // Pandoc 可执行文件路径
|
||||
PandocBinPath string // Pandoc 可执行文件路径
|
||||
PandocTemplatePath string // Pandoc Docx 模板文件路径
|
||||
)
|
||||
|
||||
func InitPandoc() {
|
||||
|
|
@ -128,6 +129,18 @@ func InitPandoc() {
|
|||
}
|
||||
}
|
||||
|
||||
PandocTemplatePath = filepath.Join(pandocDir, "pandoc-template.docx")
|
||||
if !gulu.File.IsExist(PandocTemplatePath) {
|
||||
PandocTemplatePath = filepath.Join(WorkingDir, "pandoc-template.docx")
|
||||
if "dev" == Mode || !gulu.File.IsExist(PandocTemplatePath) {
|
||||
PandocTemplatePath = filepath.Join(WorkingDir, "pandoc/pandoc-template.docx")
|
||||
}
|
||||
}
|
||||
if !gulu.File.IsExist(PandocTemplatePath) {
|
||||
PandocTemplatePath = ""
|
||||
logging.LogWarnf("pandoc template file [%s] not found", PandocTemplatePath)
|
||||
}
|
||||
|
||||
defer eventbus.Publish(EvtConfPandocInitialized)
|
||||
|
||||
if gulu.OS.IsWindows() {
|
||||
|
|
|
|||
|
|
@ -347,3 +347,96 @@ func IsPartitionRootPath(path string) bool {
|
|||
return cleanPath == "/"
|
||||
}
|
||||
}
|
||||
|
||||
// IsSensitivePath 对传入路径做统一的敏感性检测。
|
||||
func IsSensitivePath(p string) bool {
|
||||
if p == "" {
|
||||
return false
|
||||
}
|
||||
pp := filepath.Clean(strings.ToLower(p))
|
||||
|
||||
// 精确敏感文件
|
||||
exact := []string{
|
||||
"/etc/passwd",
|
||||
"/etc/shadow",
|
||||
"/etc/gshadow",
|
||||
"/var/run/secrets/kubernetes.io/serviceaccount/token",
|
||||
}
|
||||
for _, e := range exact {
|
||||
if pp == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 敏感目录前缀(UNIX 风格)
|
||||
prefixes := []string{
|
||||
"/etc/ssh",
|
||||
"/root",
|
||||
"/etc/ssl",
|
||||
"/etc/letsencrypt",
|
||||
"/var/lib/docker",
|
||||
"/.gnupg",
|
||||
"/.ssh",
|
||||
"/.aws",
|
||||
"/.kube",
|
||||
"/.docker",
|
||||
"/.config/gcloud",
|
||||
}
|
||||
for _, pre := range prefixes {
|
||||
if strings.HasPrefix(pp, pre) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Windows 常见敏感目录(小写比较)
|
||||
winPrefixes := []string{
|
||||
`c:\windows\system32`,
|
||||
`c:\windows\system`,
|
||||
`c:\users\`,
|
||||
}
|
||||
for _, wp := range winPrefixes {
|
||||
if strings.HasPrefix(pp, strings.ToLower(wp)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 文件名级别检查
|
||||
base := filepath.Base(pp)
|
||||
n := strings.ToLower(base)
|
||||
sensitiveNames := map[string]struct{}{
|
||||
".env": {},
|
||||
".env.local": {},
|
||||
".npmrc": {},
|
||||
".netrc": {},
|
||||
"id_rsa": {},
|
||||
"id_dsa": {},
|
||||
"id_ecdsa": {},
|
||||
"id_ed25519": {},
|
||||
"authorized_keys": {},
|
||||
"passwd": {},
|
||||
"shadow": {},
|
||||
"pgpass": {},
|
||||
"hosts": {},
|
||||
"credentials": {}, // 如 aws credentials
|
||||
"config.json": {}, // docker config.json 可能含 token
|
||||
}
|
||||
if _, ok := sensitiveNames[n]; ok {
|
||||
return true
|
||||
}
|
||||
// 支持 .env.* 之类的模式
|
||||
if n == ".env" || strings.HasPrefix(n, ".env.") {
|
||||
return true
|
||||
}
|
||||
|
||||
// 扩展名级别检查
|
||||
ext := strings.ToLower(filepath.Ext(n))
|
||||
sensitiveExts := []string{
|
||||
".pem", ".key", ".p12", ".pfx", ".ppk", ".asc", ".gpg",
|
||||
}
|
||||
for _, se := range sensitiveExts {
|
||||
if ext == se {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue