diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1764fb19d2..25c5bead1b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ permissions: contents: read env: - RELEASE_GO_VERSION: '1.26.5' + RELEASE_GO_VERSION: '1.26.8' concurrency: group: release-${{ github.ref_name }} diff --git a/Makefile b/Makefile index 671c0e69e2..f462578b23 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ PUBLIC_CONTENT_METADATA ?= $(QUALITY_GATE_DIR)/public-content-metadata.json LDFLAGS := -s -w -X $(MODULE)/internal/build.Version=$(VERSION) -X $(MODULE)/internal/build.Date=$(DATE) PREFIX ?= /usr/local -# The repository's Go 1.23 CI toolchain does not support -race on riscv64. +# The repository's Go 1.25 CI toolchain does not support -race on riscv64. # Prefer GOARCH passed to make (for example, `make GOARCH=riscv64 unit-test`) # over `go env GOARCH`, because command-line make variables are not visible to # $(shell ...). diff --git a/README.md b/README.md index db720a6379..444b8362f9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # lark-cli [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Go Version](https://img.shields.io/badge/go-%3E%3D1.23-blue.svg)](https://go.dev/) +[![Go Version](https://img.shields.io/badge/go-%3E%3D1.25-blue.svg)](https://go.dev/) [![npm version](https://img.shields.io/npm/v/@larksuite/cli.svg)](https://www.npmjs.com/package/@larksuite/cli) [中文版](./README.zh.md) | [English](./README.md) @@ -59,7 +59,7 @@ The official [Lark/Feishu](https://www.larksuite.com/) CLI tool, maintained by t Before you start, make sure you have: - Node.js (`npm`/`npx`) -- Go `v1.23`+ and Python 3 (only required for building from source) +- Go `v1.25`+ and Python 3 (only required for building from source) ### Quick Start (Human Users) @@ -77,7 +77,7 @@ npx @larksuite/cli@latest install **Option 2 — From source:** -Requires Go `v1.23`+ and Python 3. +Requires Go `v1.25`+ and Python 3. ```bash git clone https://github.com/larksuite/cli.git diff --git a/README.zh.md b/README.zh.md index 95722b39d6..88506c79d2 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,7 +1,7 @@ # lark-cli [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Go Version](https://img.shields.io/badge/go-%3E%3D1.23-blue.svg)](https://go.dev/) +[![Go Version](https://img.shields.io/badge/go-%3E%3D1.25-blue.svg)](https://go.dev/) [![npm version](https://img.shields.io/npm/v/@larksuite/cli.svg)](https://www.npmjs.com/package/@larksuite/cli) [中文版](./README.zh.md) | [English](./README.md) @@ -59,7 +59,7 @@ 开始之前,请确保具备以下条件: - Node.js(`npm`/`npx`) -- Go `v1.23`+ 和 Python 3(仅源码构建需要) +- Go `v1.25`+ 和 Python 3(仅源码构建需要) ### 快速开始(人类用户) @@ -77,7 +77,7 @@ npx @larksuite/cli@latest install **方式二 — 从源码安装:** -需要 Go `v1.23`+ 和 Python 3。 +需要 Go `v1.25`+ 和 Python 3。 ```bash git clone https://github.com/larksuite/cli.git diff --git a/cmd/auth/login.go b/cmd/auth/login.go index 182a6b1036..8f2fab7739 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -346,7 +346,7 @@ func authLoginRun(opts *LoginOptions, resolver domainResolver) error { return errs.NewInternalError(errs.SubtypeSDKError, "failed to write JSON output: %v", err).WithCause(err) } } else { - fmt.Fprintf(f.IOStreams.ErrOut, msg.OpenURL) + fmt.Fprint(f.IOStreams.ErrOut, msg.OpenURL) fmt.Fprintf(f.IOStreams.ErrOut, " %s\n\n", authResp.VerificationUriComplete) if f.IOStreams != nil && !f.IOStreams.IsTerminal { fmt.Fprintln(f.IOStreams.ErrOut, msg.AgentTimeoutHint(renderContext)) diff --git a/cmd/auth/login_interactive.go b/cmd/auth/login_interactive.go index 998152762e..3a8f5e9218 100644 --- a/cmd/auth/login_interactive.go +++ b/cmd/auth/login_interactive.go @@ -4,6 +4,7 @@ package auth import ( + "errors" "fmt" "sort" "strings" @@ -102,7 +103,7 @@ func runInteractiveLogin(ios *cmdutil.IOStreams, lang string, msg *loginMsg, bra Value(&selectedDomains). Validate(func(s []string) error { if len(s) == 0 { - return fmt.Errorf(msg.ErrNoDomain) + return errors.New(msg.ErrNoDomain) //nolint:forbidigo // huh inline validation text; never reaches the error envelope } return nil }), @@ -140,7 +141,7 @@ func runInteractiveLogin(ios *cmdutil.IOStreams, lang string, msg *loginMsg, bra if permLevel == "common" { permLabel = msg.PermCommonLabel } - fmt.Fprintf(ios.ErrOut, msg.Summary) + fmt.Fprint(ios.ErrOut, msg.Summary) fmt.Fprintf(ios.ErrOut, msg.SummaryDomains, strings.Join(selectedDomains, ", ")) fmt.Fprintf(ios.ErrOut, msg.SummaryPerm, permLabel) scopePreview := strings.Join(scopes, ", ") diff --git a/go.mod b/go.mod index 8839b7fc71..0c50555d71 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/larksuite/cli -go 1.23.0 +go 1.25.14 require ( github.com/Microsoft/go-winio v0.6.2 @@ -19,12 +19,12 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tidwall/gjson v1.18.0 github.com/zalando/go-keyring v0.2.8 - golang.org/x/image v0.30.0 - golang.org/x/net v0.33.0 - golang.org/x/sync v0.16.0 - golang.org/x/sys v0.33.0 - golang.org/x/term v0.27.0 - golang.org/x/text v0.28.0 + golang.org/x/image v0.45.0 + golang.org/x/net v0.58.0 + golang.org/x/sync v0.22.0 + golang.org/x/sys v0.47.0 + golang.org/x/term v0.45.0 + golang.org/x/text v0.41.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -46,7 +46,7 @@ require ( github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.6 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect diff --git a/go.sum b/go.sum index 574157832b..2a090684a8 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,9 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg= @@ -139,34 +140,34 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= -golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= +golang.org/x/image v0.45.0 h1:FMb1nTbH5H9vF55SriQHgFw5GnNL9Jg6L25BwXKzhB0= +golang.org/x/image v0.45.0/go.mod h1:n62x/7RqlwXDvGsSU4u6IUTUf6KghUZ9Bt7cG/T9Fx4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/scripts/release-workflow.test.sh b/scripts/release-workflow.test.sh index fb5d49cd92..6ac60b4d8a 100755 --- a/scripts/release-workflow.test.sh +++ b/scripts/release-workflow.test.sh @@ -49,7 +49,7 @@ jobs.each do |job_name, job| end end -expect_equal(workflow.dig("env", "RELEASE_GO_VERSION"), "1.26.5", "release Go version") +expect_equal(workflow.dig("env", "RELEASE_GO_VERSION"), "1.26.8", "release Go version") expected_jobs = %w[preflight build-sign-notarize create-draft-release verify-macos publish-github publish-npm retry-guidance] expect_equal(jobs.keys.sort, expected_jobs.sort, "release jobs") diff --git a/shortcuts/base/record_ops.go b/shortcuts/base/record_ops.go index 097a162245..1f4208c703 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -171,7 +171,7 @@ func normalizeStringList(values interface{}, opts stringListNormalizeOptions) ([ if opts.allowNil { return nil, nil } - return nil, baseFlagErrorf(opts.typeError) + return nil, baseFlagErrorf("%s", opts.typeError) case []interface{}: rawItems = typed case []string: @@ -180,13 +180,13 @@ func normalizeStringList(values interface{}, opts stringListNormalizeOptions) ([ rawItems = append(rawItems, item) } default: - return nil, baseFlagErrorf(opts.typeError) + return nil, baseFlagErrorf("%s", opts.typeError) } if len(rawItems) == 0 { if opts.allowEmpty { return nil, nil } - return nil, baseFlagErrorf(opts.emptyError) + return nil, baseFlagErrorf("%s", opts.emptyError) } if opts.max > 0 && len(rawItems) > opts.max { return nil, baseFlagErrorf("%s exceeds maximum limit of %d (got %d)", opts.limitName, opts.max, len(rawItems)) diff --git a/shortcuts/mail/draft/htmltext.go b/shortcuts/mail/draft/htmltext.go index fbaa585a84..04c0b6a2c7 100644 --- a/shortcuts/mail/draft/htmltext.go +++ b/shortcuts/mail/draft/htmltext.go @@ -19,7 +19,10 @@ import ( func plainTextFromHTML(raw string) string { doc, err := xhtml.Parse(strings.NewReader(raw)) if err != nil { - return strings.TrimSpace(raw) + // x/net/html rejects documents whose open-element stack exceeds 512 + // nodes (its stack-exhaustion CVE fix). The tokenizer has no such + // limit, so hostile nesting still yields text instead of raw markup. + return plainTextFromHTMLTokens(raw) } var buf bytes.Buffer @@ -36,9 +39,7 @@ func plainTextFromHTML(raw string) string { // all children processed — emit post-children block boundary, then pop if top.child == nil { - if isHTMLBlockBoundary(top.node) && buf.Len() > 0 && bufLastByte(&buf) != '\n' { - buf.WriteByte('\n') - } + writeBlockBoundary(&buf, top.node) stack = stack[:len(stack)-1] continue } @@ -53,19 +54,11 @@ func plainTextFromHTML(raw string) string { // emit text content if n.Type == xhtml.TextNode { - text := collapseHTMLWhitespace(n.Data) - if text != "" { - if last := bufLastByte(&buf); last != 0 && last != '\n' && last != ' ' { - buf.WriteByte(' ') - } - buf.WriteString(text) - } + writePlainText(&buf, n.Data) } // pre-children block boundary newline - if isHTMLBlockBoundary(n) && buf.Len() > 0 && bufLastByte(&buf) != '\n' { - buf.WriteByte('\n') - } + writeBlockBoundary(&buf, n) // push this node so its children get processed next if n.FirstChild != nil { @@ -73,6 +66,183 @@ func plainTextFromHTML(raw string) string { } } + return joinPlainTextLines(&buf) +} + +// plainTextFromHTMLTokens extracts text with the streaming tokenizer, which +// builds no tree and therefore has no nesting limit. It mirrors the parser's +// head handling so both paths drop the same content: everything the parser +// would place in (including an implicit head before any tag and +// head elements that appear between and ) is never emitted, the +// body starts at , at the first start tag that is not allowed in head, +// or at the first non-whitespace text, and a stray inside the body is +// ignored the way the parser ignores it. +func plainTextFromHTMLTokens(raw string) string { + w := &tokenTextWriter{} + z := xhtml.NewTokenizer(strings.NewReader(raw)) + for { + switch z.Next() { + case xhtml.ErrorToken: + return joinPlainTextLines(&w.buf) + case xhtml.StartTagToken, xhtml.SelfClosingTagToken: + w.startTag(tokenElement(z)) + case xhtml.EndTagToken: + w.endTag(tokenElement(z)) + case xhtml.TextToken: + w.text(string(z.Text())) + } + } +} + +// headPhase follows the parser's insertion modes around : text and +// head-only elements are dropped until the body starts. +type headPhase int + +const ( + beforeHead headPhase = iota + inHead + afterHead + inBody +) + +// tokenTextWriter holds the tokenizer walk state. skip lists the open +// containers whose text is never emitted, innermost last; an end tag pops +// only when its name is on the stack, so a stray inside a +//