Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rpm2docserv for openSUSE:Factory checked in at 2026-07-07 21:06:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rpm2docserv (Old) and /work/SRC/openSUSE:Factory/.rpm2docserv.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rpm2docserv" Tue Jul 7 21:06:48 2026 rev:26 rq:1364320 version:20260707.78c4c29 Changes: -------- --- /work/SRC/openSUSE:Factory/rpm2docserv/rpm2docserv.changes 2025-10-31 16:29:29.613544464 +0100 +++ /work/SRC/openSUSE:Factory/.rpm2docserv.new.1982/rpm2docserv.changes 2026-07-07 21:09:20.715039266 +0200 @@ -1,0 +2,6 @@ +Tue Jul 07 14:04:30 UTC 2026 - Thorsten Kukuk <[email protected]> + +- Update to version 20260707.78c4c29: + * Update go dependencies + +------------------------------------------------------------------- Old: ---- rpm2docserv-20251031.483a2f9.tar.xz New: ---- rpm2docserv-20260707.78c4c29.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rpm2docserv.spec ++++++ --- /var/tmp/diff_new_pack.uU2384/_old 2026-07-07 21:09:22.651106087 +0200 +++ /var/tmp/diff_new_pack.uU2384/_new 2026-07-07 21:09:22.683107192 +0200 @@ -1,7 +1,7 @@ # # spec file for package rpm2docserv # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -21,7 +21,7 @@ %endif Name: rpm2docserv -Version: 20251031.483a2f9 +Version: 20260707.78c4c29 Release: 0 Summary: Make manpages from RPMs accessible in a web browser License: Apache-2.0 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.uU2384/_old 2026-07-07 21:09:23.167123897 +0200 +++ /var/tmp/diff_new_pack.uU2384/_new 2026-07-07 21:09:23.203125140 +0200 @@ -1,7 +1,7 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/thkukuk/rpm2docserv.git</param> - <param name="changesrevision">483a2f925d713e6d57f11465c1ecd1b9a53f2b81</param></service> + <param name="changesrevision">78c4c290d5986ac6f490c9e0c03ef88888209a9b</param></service> </servicedata> (No newline at EOF) ++++++ rpm2docserv-20251031.483a2f9.tar.xz -> rpm2docserv-20260707.78c4c29.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rpm2docserv-20251031.483a2f9/go.mod new/rpm2docserv-20260707.78c4c29/go.mod --- old/rpm2docserv-20251031.483a2f9/go.mod 2025-10-31 09:31:27.000000000 +0100 +++ new/rpm2docserv-20260707.78c4c29/go.mod 2026-07-07 16:03:17.000000000 +0200 @@ -1,15 +1,13 @@ module github.com/thkukuk/rpm2docserv -go 1.24.0 - -toolchain go1.24.7 +go 1.25.0 require ( github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f - golang.org/x/net v0.46.0 - golang.org/x/sync v0.17.0 - golang.org/x/text v0.30.0 + golang.org/x/net v0.56.0 + golang.org/x/sync v0.21.0 + golang.org/x/text v0.39.0 gopkg.in/yaml.v3 v3.0.1 ) -require google.golang.org/protobuf v1.36.10 +require google.golang.org/protobuf v1.36.11 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rpm2docserv-20251031.483a2f9/go.sum new/rpm2docserv-20260707.78c4c29/go.sum --- old/rpm2docserv-20251031.483a2f9/go.sum 2025-10-31 09:31:27.000000000 +0100 +++ new/rpm2docserv-20260707.78c4c29/go.sum 2026-07-07 16:03:17.000000000 +0200 @@ -2,14 +2,14 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g= github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/context/context.go new/vendor/golang.org/x/net/context/context.go --- old/vendor/golang.org/x/net/context/context.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/context/context.go 2026-07-07 16:03:17.000000000 +0200 @@ -2,42 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package context defines the Context type, which carries deadlines, -// cancellation signals, and other request-scoped values across API boundaries -// and between processes. -// As of Go 1.7 this package is available in the standard library under the -// name [context]. +// Package context has been superseded by the standard library [context] package. // -// Incoming requests to a server should create a [Context], and outgoing -// calls to servers should accept a Context. The chain of function -// calls between them must propagate the Context, optionally replacing -// it with a derived Context created using [WithCancel], [WithDeadline], -// [WithTimeout], or [WithValue]. -// -// Programs that use Contexts should follow these rules to keep interfaces -// consistent across packages and enable static analysis tools to check context -// propagation: -// -// Do not store Contexts inside a struct type; instead, pass a Context -// explicitly to each function that needs it. This is discussed further in -// https://go.dev/blog/context-and-structs. The Context should be the first -// parameter, typically named ctx: -// -// func DoSomething(ctx context.Context, arg Arg) error { -// // ... use ctx ... -// } -// -// Do not pass a nil [Context], even if a function permits it. Pass [context.TODO] -// if you are unsure about which Context to use. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -// -// The same Context may be passed to functions running in different goroutines; -// Contexts are safe for simultaneous use by multiple goroutines. -// -// See https://go.dev/blog/context for example code for a server that uses -// Contexts. +// Deprecated: Use the standard library context package instead. package context import ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/entity.go new/vendor/golang.org/x/net/html/entity.go --- old/vendor/golang.org/x/net/html/entity.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/entity.go 2026-07-07 16:03:17.000000000 +0200 @@ -2156,9 +2156,8 @@ // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, + "nLt;": {'\u226A', '\u20D2'}, + "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/escape.go new/vendor/golang.org/x/net/html/escape.go --- old/vendor/golang.org/x/net/html/escape.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/escape.go 2026-07-07 16:03:17.000000000 +0200 @@ -6,6 +6,7 @@ import ( "bytes" + "slices" "strings" "unicode/utf8" ) @@ -50,25 +51,24 @@ // 0x0D->'\u000D' is a no-op. } -// unescapeEntity reads an entity like "<" from b[src:] and writes the -// corresponding "<" to b[dst:], returning the incremented dst and src cursors. -// Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +// unescapeEntity attempts to consume a character reference from s[src:], +// returning the rune, potential second rune, and number of bytes consumed +// (which indicates the length of the character reference). It is assumed that +// the first byte of s is '&'. attribute should be true if parsing an attribute +// value. +func unescapeEntity(s []byte, attribute bool) (rune, rune, int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. - i, s := 1, b[src:] + i := 1 if len(s) <= 1 { - b[dst] = b[src] - return dst + 1, src + 1 + return '&', 0, 1 } if s[i] == '#' { - if len(s) <= 3 { // We need to have at least "&#.". - b[dst] = b[src] - return dst + 1, src + 1 + if len(s) <= 2 { // We need to have at least "&#". + return '&', 0, 1 } i++ c := s[i] @@ -78,34 +78,43 @@ i++ } + i0 := i x := '\x00' for i < len(s) { c = s[i] - i++ + var d rune + var mult rune if hex { + mult = 16 if '0' <= c && c <= '9' { - x = 16*x + rune(c) - '0' - continue + d = rune(c) - '0' } else if 'a' <= c && c <= 'f' { - x = 16*x + rune(c) - 'a' + 10 - continue + d = rune(c) - 'a' + 10 } else if 'A' <= c && c <= 'F' { - x = 16*x + rune(c) - 'A' + 10 - continue + d = rune(c) - 'A' + 10 + } else { + break + } + } else { + mult = 10 + if '0' <= c && c <= '9' { + d = rune(c) - '0' + } else { + break } - } else if '0' <= c && c <= '9' { - x = 10*x + rune(c) - '0' - continue } - if c != ';' { - i-- + if x <= 0x10FFFF { + x = mult*x + d } - break + i++ } - if i <= 3 { // No characters matched. - b[dst] = b[src] - return dst + 1, src + 1 + if i == i0 { // No characters matched. + return '&', 0, 1 + } + + if i < len(s) && s[i] == ';' { + i++ } if 0x80 <= x && x <= 0x9F { @@ -116,7 +125,7 @@ x = '\uFFFD' } - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } // Consume the maximum number of characters possible, with the @@ -141,10 +150,9 @@ } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } else if x := entity2[entityName]; x[0] != 0 { - dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) - return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + return x[0], x[1], i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { @@ -152,35 +160,67 @@ } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + return x, 0, j + 1 } } } - dst1, src1 = dst+i, src+i - copy(b[dst:dst1], b[src:src1]) - return dst1, src1 + return '&', 0, 1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a<b". -// attribute should be true if parsing an attribute value. +// unescape unescapes b's entites, so that "a<b" becomes "a<b". It attempts +// to do so in place, but if the unescaped value is longer than the input it +// allocates a new slice. attribute should be true if parsing an attribute +// value. func unescape(b []byte, attribute bool) []byte { - for i, c := range b { - if c == '&' { - dst, src := unescapeEntity(b, i, i, attribute) - for src < len(b) { - c := b[src] - if c == '&' { - dst, src = unescapeEntity(b, dst, src, attribute) - } else { - b[dst] = c - dst, src = dst+1, src+1 - } + firstAmp := slices.Index(b, '&') + if firstAmp == -1 { + return b + } + + out := b[:firstAmp] + src := firstAmp + reusingB := true + for src < len(b) { + if b[src] != '&' { + out = append(out, b[src]) + src++ + continue + } + + r1, r2, entityNameLen := unescapeEntity(b[src:], attribute) + if entityNameLen == 1 && r1 == '&' { + // Not an entity + out = append(out, '&') + src++ + continue + } + + // Compute replacement length + replLen := utf8.RuneLen(r1) + if r2 != 0 { + replLen += utf8.RuneLen(r2) + } + + // If the name of the entity is shorter than the width of the + // replacement runes then we need to expand the output slice to + // fit the replacement. + if replLen > entityNameLen { + if reusingB { + out = slices.Clone(out) + reusingB = false } - return b[0:dst] + out = slices.Grow(out, replLen) } + out = utf8.AppendRune(out, r1) + if r2 != 0 { + out = utf8.AppendRune(out, r2) + } + + src += entityNameLen } - return b + + return out } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/foreign.go new/vendor/golang.org/x/net/html/foreign.go --- old/vendor/golang.org/x/net/html/foreign.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/foreign.go 2026-07-07 16:03:17.000000000 +0200 @@ -23,7 +23,7 @@ } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + "xlink:title", "xlink:type", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/iter.go new/vendor/golang.org/x/net/html/iter.go --- old/vendor/golang.org/x/net/html/iter.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/iter.go 2026-07-07 16:03:17.000000000 +0200 @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.23 - package html import "iter" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/node.go new/vendor/golang.org/x/net/html/node.go --- old/vendor/golang.org/x/net/html/node.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/node.go 2026-07-07 16:03:17.000000000 +0200 @@ -11,6 +11,7 @@ // A NodeType is the type of a Node. type NodeType uint32 +//go:generate stringer -type NodeType const ( ErrorNode NodeType = iota TextNode diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/nodetype_string.go new/vendor/golang.org/x/net/html/nodetype_string.go --- old/vendor/golang.org/x/net/html/nodetype_string.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/html/nodetype_string.go 2026-07-07 16:03:17.000000000 +0200 @@ -0,0 +1,31 @@ +// Code generated by "stringer -type NodeType"; DO NOT EDIT. + +package html + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ErrorNode-0] + _ = x[TextNode-1] + _ = x[DocumentNode-2] + _ = x[ElementNode-3] + _ = x[CommentNode-4] + _ = x[DoctypeNode-5] + _ = x[RawNode-6] + _ = x[scopeMarkerNode-7] +} + +const _NodeType_name = "ErrorNodeTextNodeDocumentNodeElementNodeCommentNodeDoctypeNodeRawNodescopeMarkerNode" + +var _NodeType_index = [...]uint8{0, 9, 17, 29, 40, 51, 62, 69, 84} + +func (i NodeType) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_NodeType_index)-1 { + return "NodeType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _NodeType_name[_NodeType_index[idx]:_NodeType_index[idx+1]] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/parse.go new/vendor/golang.org/x/net/html/parse.go --- old/vendor/golang.org/x/net/html/parse.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/parse.go 2026-07-07 16:03:17.000000000 +0200 @@ -5,9 +5,11 @@ package html import ( + "cmp" "errors" "fmt" "io" + "slices" "strings" a "golang.org/x/net/html/atom" @@ -61,7 +63,7 @@ // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ - "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template, a.Select}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } @@ -76,7 +78,6 @@ tableScope tableRowScope tableBodyScope - selectScope ) // popUntil pops the stack of open elements at the highest element whose tag @@ -131,10 +132,6 @@ if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } - case selectScope: - if tagAtom != a.Optgroup && tagAtom != a.Option { - return -1 - } default: panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } @@ -328,6 +325,14 @@ }) } +func attrCompare(a, b Attribute) int { + return cmp.Or( + cmp.Compare(a.Namespace, b.Namespace), + cmp.Compare(a.Key, b.Key), + cmp.Compare(a.Val, b.Val), + ) +} + // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ @@ -343,6 +348,10 @@ tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() + // In order to optimize the search, we need the attributes to be sorted, so we + // can just use slices.Equal. + slices.SortFunc(attr, attrCompare) + // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: @@ -360,19 +369,7 @@ if n.DataAtom != tagAtom { continue } - if len(n.Attr) != len(attr) { - continue - } - compareAttributes: - for _, t0 := range n.Attr { - for _, t1 := range attr { - if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { - // Found a match for this attribute, continue with the next attribute. - continue compareAttributes - } - } - // If we get here, there is no attribute that matches a. - // Therefore the element is not identical to the new one. + if !slices.Equal(n.Attr, attr) { continue findIdenticalElements } @@ -382,7 +379,11 @@ } } - p.afe = append(p.afe, p.top()) + // Sort the attributes to optimize future identical-element searches. + top := p.top() + slices.SortFunc(top.Attr, attrCompare) + + p.afe = append(p.afe, top) } // Section 12.2.4.3. @@ -454,21 +455,6 @@ } switch n.DataAtom { - case a.Select: - if !last { - for ancestor, first := n, p.oe[0]; ancestor != first; { - ancestor = p.oe[p.oe.index(ancestor)-1] - switch ancestor.DataAtom { - case a.Template: - p.im = inSelectIM - return - case a.Table: - p.im = inSelectInTableIM - return - } - } - } - p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // @@ -996,7 +982,10 @@ p.popUntil(buttonScope, a.P) p.addElement() case a.Button: - p.popUntil(defaultScope, a.Button) + if p.elementInScope(defaultScope, a.Button) { + p.generateImpliedEndTags() + p.popUntil(defaultScope, a.Button) + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false @@ -1034,7 +1023,18 @@ p.framesetOK = false p.im = inTableIM return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: + case a.Area, a.Br, a.Embed, a.Img, a.Keygen, a.Wbr: + p.reconstructActiveFormattingElements() + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case a.Input: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } + p.popUntil(defaultScope, a.Select) p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() @@ -1055,7 +1055,13 @@ p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: - p.popUntil(buttonScope, a.P) + if p.elementInScope(buttonScope, a.P) { + p.generateImpliedEndTags("p") + p.popUntil(defaultScope, a.P) + } + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + } p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() @@ -1089,13 +1095,30 @@ // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() case a.Select: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } else if p.popUntil(defaultScope, a.Select) { + return true + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false - p.im = inSelectIM return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { + case a.Option: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags("optgroup") + // If oe has option element in scope, parse error? + } else if p.top().DataAtom == a.Option { + p.oe.pop() + } + p.reconstructActiveFormattingElements() + p.addElement() + case a.Optgroup: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + // If oe has option or optgroup element in scope, parse error? + } else if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() @@ -1143,7 +1166,12 @@ return false } return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Select, a.Summary, a.Ul: + if !p.elementInScope(defaultScope, p.tok.DataAtom) { + // Ignore the token. + return true + } + p.generateImpliedEndTags() p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { @@ -1372,8 +1400,6 @@ } // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. -// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content -// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { for i := len(p.oe) - 1; i >= 0; i-- { // Two element nodes have the same tag if they have the same Data (a @@ -1383,7 +1409,7 @@ // Uncommon (custom) tags get a zero DataAtom. // // The if condition here is equivalent to (p.oe[i].Data == tagName). - if (p.oe[i].DataAtom == tagAtom) && + if p.oe[i].Namespace == "" && (p.oe[i].DataAtom == tagAtom) && ((tagAtom != 0) || (p.oe[i].Data == tagName)) { p.oe = p.oe[:i] break @@ -1484,17 +1510,6 @@ } p.addElement() p.form = p.oe.pop() - case a.Select: - p.reconstructActiveFormattingElements() - switch p.top().DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParenting = true - } - p.addElement() - p.fosterParenting = false - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1543,12 +1558,6 @@ p.clearActiveFormattingElements() p.im = inTableIM return false - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1758,12 +1767,6 @@ } // Ignore the token. return true - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1794,118 +1797,6 @@ return inBodyIM(p) } -// Section 12.2.6.4.16. -func inSelectIM(p *parser) bool { - switch p.tok.Type { - case TextToken: - p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - return inBodyIM(p) - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.addElement() - case a.Optgroup: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - if p.top().DataAtom == a.Optgroup { - p.oe.pop() - } - p.addElement() - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Input, a.Keygen, a.Textarea: - if p.elementInScope(selectScope, a.Select) { - p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) - return false - } - // In order to properly ignore <textarea>, we need to change the tokenizer mode. - p.tokenizer.NextIsNotRawText() - // Ignore the token. - return true - case a.Script, a.Template: - return inHeadIM(p) - case a.Iframe, a.Noembed, a.Noframes, a.Noscript, a.Plaintext, a.Style, a.Title, a.Xmp: - // Don't let the tokenizer go into raw text mode when there are raw tags - // to be ignored. These tags should be ignored from the tokenizer - // properly. - p.tokenizer.NextIsNotRawText() - // Ignore the token. - return true - } - case EndTagToken: - switch p.tok.DataAtom { - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - case a.Optgroup: - i := len(p.oe) - 1 - if p.oe[i].DataAtom == a.Option { - i-- - } - if p.oe[i].DataAtom == a.Optgroup { - p.oe = p.oe[:i] - } - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Template: - return inHeadIM(p) - } - case CommentToken: - p.addChild(&Node{ - Type: CommentNode, - Data: p.tok.Data, - }) - case DoctypeToken: - // Ignore the token. - return true - case ErrorToken: - return inBodyIM(p) - } - - return true -} - -// Section 12.2.6.4.17. -func inSelectInTableIM(p *parser) bool { - switch p.tok.Type { - case StartTagToken, EndTagToken: - switch p.tok.DataAtom { - case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th: - if p.tok.Type == EndTagToken && !p.elementInScope(tableScope, p.tok.DataAtom) { - // Ignore the token. - return true - } - // This is like p.popUntil(selectScope, a.Select), but it also - // matches <math select>, not just <select>. Matching the MathML - // tag is arguably incorrect (conceptually), but it mimics what - // Chromium does. - for i := len(p.oe) - 1; i >= 0; i-- { - if n := p.oe[i]; n.DataAtom == a.Select { - p.oe = p.oe[:i] - break - } - } - p.resetInsertionMode() - return false - } - } - return inSelectIM(p) -} - // Section 12.2.6.4.18. func inTemplateIM(p *parser) bool { switch p.tok.Type { @@ -2170,7 +2061,7 @@ const whitespaceOrNUL = whitespace + "\x00" -// Section 12.2.6.5 +// Section 13.2.6.5 func parseForeignContent(p *parser) bool { switch p.tok.Type { case TextToken: @@ -2185,28 +2076,26 @@ Data: p.tok.Data, }) case StartTagToken: - if !p.fragment { - b := breakout[p.tok.Data] - if p.tok.DataAtom == a.Font { - loop: - for _, attr := range p.tok.Attr { - switch attr.Key { - case "color", "face", "size": - b = true - break loop - } + b := breakout[p.tok.Data] + if p.tok.DataAtom == a.Font { + loop: + for _, attr := range p.tok.Attr { + switch attr.Key { + case "color", "face", "size": + b = true + break loop } } - if b { - for i := len(p.oe) - 1; i >= 0; i-- { - n := p.oe[i] - if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) { - p.oe = p.oe[:i+1] - break - } + } + if b { + for i := len(p.oe) - 1; i >= 0; i-- { + n := p.oe[i] + if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) { + p.oe = p.oe[:i+1] + break } - return false } + return p.im(p) } current := p.adjustedCurrentNode() switch current.Namespace { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/render.go new/vendor/golang.org/x/net/html/render.go --- old/vendor/golang.org/x/net/html/render.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/render.go 2026-07-07 16:03:17.000000000 +0200 @@ -113,14 +113,14 @@ if _, err := w.WriteString(" PUBLIC "); err != nil { return err } - if err := writeQuoted(w, p); err != nil { + if err := writeDoctypeQuoted(w, p); err != nil { return err } if s != "" { if err := w.WriteByte(' '); err != nil { return err } - if err := writeQuoted(w, s); err != nil { + if err := writeDoctypeQuoted(w, s); err != nil { return err } } @@ -128,7 +128,7 @@ if _, err := w.WriteString(" SYSTEM "); err != nil { return err } - if err := writeQuoted(w, s); err != nil { + if err := writeDoctypeQuoted(w, s); err != nil { return err } } @@ -243,27 +243,50 @@ if n.Namespace != "" { return false } + switch n.Data { case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": + // We need to check if n is a node that was fostered from a HTML namespace + // into a non-HTML namespace (in which case, different rules apply to it). + // We do this by walking up the tree until we find a node with a non-empty + // namespace. If we find such a node, we also have to check if it's + // an HTML integration point. If it isn't, then the node we're currently + // looking at is foster-parented and we should return false. + for p := n.Parent; p != nil; p = p.Parent { + if p.Namespace != "" { + if !htmlIntegrationPoint(p) { + return false + } + break + } + } + return true default: return false } } -// writeQuoted writes s to w surrounded by quotes. Normally it will use double +// writeDoctypeQuoted writes s to w surrounded by quotes. Normally it will use double // quotes, but if s contains a double quote, it will use single quotes. +// If s contains any '>' characters, they are replaced with > in order +// to prevent triggering an abrupt-doctype-system-identifier parse error. // It is used for writing the identifiers in a doctype declaration. // In valid HTML, they can't contain both types of quotes. -func writeQuoted(w writer, s string) error { +func writeDoctypeQuoted(w writer, s string) error { var q byte = '"' if strings.Contains(s, `"`) { + // parseDoctype will never produce a Node with both quote types, but a user + // can construct their own Node that violates this assumption. + if strings.Contains(s, `'`) { + return errors.New("doctype contains both quote types, cannot be safely rendered") + } q = '\'' } if err := w.WriteByte(q); err != nil { return err } - if _, err := w.WriteString(s); err != nil { + if _, err := w.WriteString(strings.ReplaceAll(s, ">", ">")); err != nil { return err } if err := w.WriteByte(q); err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/html/token.go new/vendor/golang.org/x/net/html/token.go --- old/vendor/golang.org/x/net/html/token.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/net/html/token.go 2026-07-07 16:03:17.000000000 +0200 @@ -156,6 +156,7 @@ // incremented on each call to TagAttr. pendingAttr [2]span attr [][2]span + attrNames map[string]bool nAttrReturned int // rawTag is the "script" in "</script>" that closes the next token. If // non-empty, the subsequent call to Next will return a raw or RCDATA text @@ -703,7 +704,11 @@ for i := 0; i < 2; i++ { c[i] = z.readByte() if z.err != nil { + // bogus comment z.data.end = z.raw.end + if i == 1 && c[0] == '>' { + z.data.end-- + } return CommentToken } } @@ -731,6 +736,13 @@ for i := 0; i < len(s); i++ { c := z.readByte() if z.err != nil { + if z.err == io.EOF { + // Back up to read the fragment of "DOCTYPE" again, reset + // z.err to signal EOF on the next call + z.raw.end = z.data.start + z.err = nil + return false + } z.data.end = z.raw.end return false } @@ -756,6 +768,13 @@ for i := 0; i < len(s); i++ { c := z.readByte() if z.err != nil { + if z.err == io.EOF { + // Back up to read the fragment of "[CDATA[" again, reset + // z.err to signal EOF on the next call + z.raw.end = z.data.start + z.err = nil + return false + } z.data.end = z.raw.end return false } @@ -867,6 +886,7 @@ func (z *Tokenizer) readTag(saveAttr bool) { z.attr = z.attr[:0] z.nAttrReturned = 0 + clear(z.attrNames) // Read the tag name and attribute key/value pairs. z.readTagName() if z.skipWhiteSpace(); z.err != nil { @@ -880,9 +900,11 @@ z.raw.end-- z.readTagAttrKey() z.readTagAttrVal() - // Save pendingAttr if saveAttr and that attribute has a non-empty key. - if saveAttr && z.pendingAttr[0].start != z.pendingAttr[0].end { + // Save pendingAttr if saveAttr and that attribute has a non-empty key, and the key hasn't been seen before. + key := string(lower(bytes.Clone(z.buf[z.pendingAttr[0].start:z.pendingAttr[0].end]))) + if saveAttr && z.pendingAttr[0].start != z.pendingAttr[0].end && !z.attrNames[key] { z.attr = append(z.attr, z.pendingAttr) + z.attrNames[key] = true } if z.skipWhiteSpace(); z.err != nil { break @@ -1202,7 +1224,7 @@ if z.data.start < z.data.end { switch z.tt { case StartTagToken, EndTagToken, SelfClosingTagToken: - s := z.buf[z.data.start:z.data.end] + s := bytes.ReplaceAll(z.buf[z.data.start:z.data.end], nul, replacement) z.data.start = z.raw.end z.data.end = z.raw.end return lower(s), z.nAttrReturned < len(z.attr) @@ -1220,8 +1242,8 @@ case StartTagToken, SelfClosingTagToken: x := z.attr[z.nAttrReturned] z.nAttrReturned++ - key = z.buf[x[0].start:x[0].end] - val = z.buf[x[1].start:x[1].end] + key = bytes.ReplaceAll(z.buf[x[0].start:x[0].end], nul, replacement) + val = bytes.ReplaceAll(z.buf[x[1].start:x[1].end], nul, replacement) return lower(key), unescape(convertNewlines(val), true), z.nAttrReturned < len(z.attr) } } @@ -1273,13 +1295,27 @@ // The input is assumed to be UTF-8 encoded. func NewTokenizerFragment(r io.Reader, contextTag string) *Tokenizer { z := &Tokenizer{ - r: r, - buf: make([]byte, 0, 4096), + r: r, + buf: make([]byte, 0, 4096), + attrNames: make(map[string]bool), } if contextTag != "" { + // Per the "Parsing HTML Fragments" portion of the spec: + // For performance reasons, an implementation that does not report errors + // and that uses the actual state machine described in this specification + // directly could use the PLAINTEXT state instead of the RAWTEXT and script + // data states where those are mentioned in the list above. Except for + // rules regarding parse errors, they are equivalent, since there is no + // appropriate end tag token in the fragment case, yet they involve far + // fewer state transitions. + // + // As such we just set everything to plaintext, which makes some complex parsing + // cases somewhat simpler. switch s := strings.ToLower(contextTag); s { - case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "title", "textarea", "xmp": + case "title", "textarea": z.rawTag = s + case "style", "xmp", "iframe", "noembed", "noframes", "script", "noscript", "plaintext": + z.rawTag = "plaintext" } } return z diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/sync/errgroup/errgroup.go new/vendor/golang.org/x/sync/errgroup/errgroup.go --- old/vendor/golang.org/x/sync/errgroup/errgroup.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/golang.org/x/sync/errgroup/errgroup.go 2026-07-07 16:03:17.000000000 +0200 @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package errgroup provides synchronization, error propagation, and Context -// cancelation for groups of goroutines working on subtasks of a common task. +// cancellation for groups of goroutines working on subtasks of a common task. // // [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks // returning errors. @@ -109,7 +109,7 @@ if g.sem != nil { select { case g.sem <- token{}: - // Note: this allows barging iff channels in general allow barging. + // Note: this allows barging if and only if channels in general allow barging. default: return false } @@ -144,8 +144,8 @@ g.sem = nil return } - if len(g.sem) != 0 { - panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) + if active := len(g.sem); active != 0 { + panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", active)) } g.sem = make(chan token, n) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go new/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go --- old/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go 2026-07-07 16:03:17.000000000 +0200 @@ -32,7 +32,7 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { f := new(filedesc.Field) f.L0.ParentFile = filedesc.SurrogateProto2 - f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + packed := false for len(tag) > 0 { i := strings.IndexByte(tag, ',') if i < 0 { @@ -108,7 +108,7 @@ f.L1.StringName.InitJSON(jsonName) } case s == "packed": - f.L1.EditionFeatures.IsPacked = true + packed = true case strings.HasPrefix(s, "def="): // The default tag is special in that everything afterwards is the // default regardless of the presence of commas. @@ -121,6 +121,13 @@ tag = strings.TrimPrefix(tag[i:], ",") } + // Update EditionFeatures after the loop and after we know whether this is + // a proto2 or proto3 field. + f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + if packed { + f.L1.EditionFeatures.IsPacked = true + } + // The generator uses the group message name instead of the field name. // We obtain the real field name by lowercasing the group name. if f.L1.Kind == protoreflect.GroupKind { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go new/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go --- old/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go 2026-07-07 16:03:17.000000000 +0200 @@ -424,27 +424,34 @@ return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in)) } -// parseTypeName parses Any type URL or extension field name. The name is -// enclosed in [ and ] characters. The C++ parser does not handle many legal URL -// strings. This implementation is more liberal and allows for the pattern -// ^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`). Whitespaces and comments are allowed -// in between [ ], '.', '/' and the sub names. +// parseTypeName parses an Any type URL or an extension field name. The name is +// enclosed in [ and ] characters. We allow almost arbitrary type URL prefixes, +// closely following the text-format spec [1,2]. We implement "ExtensionName | +// AnyName" as follows (with some exceptions for backwards compatibility): +// +// char = [-_a-zA-Z0-9] +// url_char = char | [.~!$&'()*+,;=] | "%", hex, hex +// +// Ident = char, { char } +// TypeName = Ident, { ".", Ident } ; +// UrlPrefix = url_char, { url_char | "/" } ; +// ExtensionName = "[", TypeName, "]" ; +// AnyName = "[", UrlPrefix, "/", TypeName, "]" ; +// +// Additionally, we allow arbitrary whitespace and comments between [ and ]. +// +// [1] https://protobuf.dev/reference/protobuf/textformat-spec/#characters +// [2] https://protobuf.dev/reference/protobuf/textformat-spec/#field-names func (d *Decoder) parseTypeName() (Token, error) { - startPos := len(d.orig) - len(d.in) // Use alias s to advance first in order to use d.in for error handling. - // Caller already checks for [ as first character. + // Caller already checks for [ as first character (d.in[0] == '['). s := consume(d.in[1:], 0) if len(s) == 0 { return Token{}, ErrUnexpectedEOF } + // Collect everything between [ and ] in name. var name []byte - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] - } - s = consume(s, 0) - var closed bool for len(s) > 0 && !closed { switch { @@ -452,23 +459,20 @@ s = s[1:] closed = true - case s[0] == '/', s[0] == '.': - if len(name) > 0 && (name[len(name)-1] == '/' || name[len(name)-1] == '.') { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)+1]) - } + case s[0] == '/' || isTypeNameChar(s[0]) || isUrlExtraChar(s[0]): name = append(name, s[0]) - s = s[1:] - s = consume(s, 0) - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] + s = consume(s[1:], 0) + + // URL percent-encoded chars + case s[0] == '%': + if len(s) < 3 || !isHexChar(s[1]) || !isHexChar(s[2]) { + return Token{}, d.parseTypeNameError(s, 3) } - s = consume(s, 0) + name = append(name, s[0], s[1], s[2]) + s = consume(s[3:], 0) default: - return Token{}, d.newSyntaxError( - "invalid type URL/extension field name: %s", d.orig[startPos:len(d.orig)-len(s)+1]) + return Token{}, d.parseTypeNameError(s, 1) } } @@ -476,15 +480,38 @@ return Token{}, ErrUnexpectedEOF } - // First character cannot be '.'. Last character cannot be '.' or '/'. - size := len(name) - if size == 0 || name[0] == '.' || name[size-1] == '.' || name[size-1] == '/' { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)]) + // Split collected name on last '/' into urlPrefix and typeName (if '/' is + // present). + typeName := name + if i := bytes.LastIndexByte(name, '/'); i != -1 { + urlPrefix := name[:i] + typeName = name[i+1:] + + // urlPrefix may be empty (for backwards compatibility). + // If non-empty, it must not start with '/'. + if len(urlPrefix) > 0 && urlPrefix[0] == '/' { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + // typeName must not be empty (note: "" splits to [""]) and all identifier + // parts must not be empty. + for _, ident := range bytes.Split(typeName, []byte{'.'}) { + if len(ident) == 0 { + return Token{}, d.parseTypeNameError(s, 0) + } } + // typeName must not contain any percent-encoded or special URL chars. + for _, b := range typeName { + if b == '%' || (b != '.' && isUrlExtraChar(b)) { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + startPos := len(d.orig) - len(d.in) + endPos := len(d.orig) - len(s) d.in = s - endPos := len(d.orig) - len(d.in) d.consume(0) return Token{ @@ -496,16 +523,32 @@ }, nil } +func (d *Decoder) parseTypeNameError(s []byte, numUnconsumedChars int) error { + return d.newSyntaxError( + "invalid type URL/extension field name: %s", + d.in[:len(d.in)-len(s)+min(numUnconsumedChars, len(s))], + ) +} + +func isHexChar(b byte) bool { + return ('0' <= b && b <= '9') || + ('a' <= b && b <= 'f') || + ('A' <= b && b <= 'F') +} + func isTypeNameChar(b byte) bool { - return (b == '-' || b == '_' || + return b == '-' || b == '_' || ('0' <= b && b <= '9') || ('a' <= b && b <= 'z') || - ('A' <= b && b <= 'Z')) + ('A' <= b && b <= 'Z') } -func isWhiteSpace(b byte) bool { +// isUrlExtraChar complements isTypeNameChar with extra characters that we allow +// in URLs but not in type names. Note that '/' is not included so that it can +// be treated specially. +func isUrlExtraChar(b byte) bool { switch b { - case ' ', '\n', '\r', '\t': + case '.', '~', '!', '$', '&', '(', ')', '*', '+', ',', ';', '=': return true default: return false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/filedesc/desc.go new/vendor/google.golang.org/protobuf/internal/filedesc/desc.go --- old/vendor/google.golang.org/protobuf/internal/filedesc/desc.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/filedesc/desc.go 2026-07-07 16:03:17.000000000 +0200 @@ -32,6 +32,7 @@ EditionProto3 Edition = 999 Edition2023 Edition = 1000 Edition2024 Edition = 1001 + EditionUnstable Edition = 9999 EditionUnsupported Edition = 100000 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go new/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go --- old/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go 2026-07-07 16:03:17.000000000 +0200 @@ -330,7 +330,6 @@ md.L1.Extensions.List[extensionIdx].unmarshalFull(v, sb) extensionIdx++ case genid.DescriptorProto_Options_field_number: - md.unmarshalOptions(v) rawOptions = appendOptions(rawOptions, v) } default: @@ -356,27 +355,6 @@ md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions) } -func (md *Message) unmarshalOptions(b []byte) { - for len(b) > 0 { - num, typ, n := protowire.ConsumeTag(b) - b = b[n:] - switch typ { - case protowire.VarintType: - v, m := protowire.ConsumeVarint(b) - b = b[m:] - switch num { - case genid.MessageOptions_MapEntry_field_number: - md.L1.IsMapEntry = protowire.DecodeBool(v) - case genid.MessageOptions_MessageSetWireFormat_field_number: - md.L1.IsMessageSet = protowire.DecodeBool(v) - } - default: - m := protowire.ConsumeFieldValue(num, typ, b) - b = b[m:] - } - } -} - func unmarshalMessageReservedRange(b []byte) (r [2]protoreflect.FieldNumber) { for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go new/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go --- old/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go 2026-07-07 16:03:17.000000000 +0200 @@ -26,6 +26,7 @@ Edition_EDITION_PROTO3_enum_value = 999 Edition_EDITION_2023_enum_value = 1000 Edition_EDITION_2024_enum_value = 1001 + Edition_EDITION_UNSTABLE_enum_value = 9999 Edition_EDITION_1_TEST_ONLY_enum_value = 1 Edition_EDITION_2_TEST_ONLY_enum_value = 2 Edition_EDITION_99997_TEST_ONLY_enum_value = 99997 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/impl/codec_map.go new/vendor/google.golang.org/protobuf/internal/impl/codec_map.go --- old/vendor/google.golang.org/protobuf/internal/impl/codec_map.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/impl/codec_map.go 2026-07-07 16:03:17.000000000 +0200 @@ -113,6 +113,9 @@ } func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } @@ -170,6 +173,9 @@ } func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/impl/decode.go new/vendor/google.golang.org/protobuf/internal/impl/decode.go --- old/vendor/google.golang.org/protobuf/internal/impl/decode.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/impl/decode.go 2026-07-07 16:03:17.000000000 +0200 @@ -102,8 +102,7 @@ func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) { mi.init() - opts.depth-- - if opts.depth < 0 { + if opts.depth--; opts.depth < 0 { return out, errRecursionDepth } if flags.ProtoLegacy && mi.isMessageSet { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/impl/validate.go new/vendor/google.golang.org/protobuf/internal/impl/validate.go --- old/vendor/google.golang.org/protobuf/internal/impl/validate.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/impl/validate.go 2026-07-07 16:03:17.000000000 +0200 @@ -68,9 +68,13 @@ if in.Resolver == nil { in.Resolver = protoregistry.GlobalTypes } + if in.Depth == 0 { + in.Depth = protowire.DefaultRecursionLimit + } o, st := mi.validate(in.Buf, 0, unmarshalOptions{ flags: in.Flags, resolver: in.Resolver, + depth: in.Depth, }) if o.initialized { out.Flags |= protoiface.UnmarshalInitialized @@ -257,6 +261,9 @@ states[0].typ = validationTypeGroup states[0].endGroup = groupTag } + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } initialized := true start := len(b) State: @@ -451,6 +458,13 @@ mi: vi.mi, tail: b, }) + if vi.typ == validationTypeMessage || + vi.typ == validationTypeGroup || + vi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State case validationTypeRepeatedVarint: @@ -499,6 +513,9 @@ mi: vi.mi, endGroup: num, }) + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } continue State case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem: typeid, v, n, err := messageset.ConsumeFieldValue(b, false) @@ -521,6 +538,13 @@ mi: xvi.mi, tail: b[n:], }) + if xvi.typ == validationTypeMessage || + xvi.typ == validationTypeGroup || + xvi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State } @@ -547,12 +571,14 @@ switch st.typ { case validationTypeMessage, validationTypeGroup: numRequiredFields = int(st.mi.numRequiredFields) + opts.depth++ case validationTypeMap: // If this is a map field with a message value that contains // required fields, require that the value be present. if st.mi != nil && st.mi.numRequiredFields > 0 { numRequiredFields = 1 } + opts.depth++ } // If there are more than 64 required fields, this check will // always fail and we will report that the message is potentially diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/internal/version/version.go new/vendor/google.golang.org/protobuf/internal/version/version.go --- old/vendor/google.golang.org/protobuf/internal/version/version.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/internal/version/version.go 2026-07-07 16:03:17.000000000 +0200 @@ -52,7 +52,7 @@ const ( Major = 1 Minor = 36 - Patch = 10 + Patch = 11 PreRelease = "" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/google.golang.org/protobuf/proto/decode.go new/vendor/google.golang.org/protobuf/proto/decode.go --- old/vendor/google.golang.org/protobuf/proto/decode.go 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/google.golang.org/protobuf/proto/decode.go 2026-07-07 16:03:17.000000000 +0200 @@ -121,9 +121,8 @@ out, err = methods.Unmarshal(in) } else { - o.RecursionLimit-- - if o.RecursionLimit < 0 { - return out, errors.New("exceeded max recursion depth") + if o.RecursionLimit--; o.RecursionLimit < 0 { + return out, errRecursionDepth } err = o.unmarshalMessageSlow(b, m) } @@ -220,6 +219,9 @@ } func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) { + if o.RecursionLimit--; o.RecursionLimit < 0 { + return 0, errRecursionDepth + } if wtyp != protowire.BytesType { return 0, errUnknown } @@ -305,3 +307,5 @@ var errUnknown = errors.New("BUG: internal error (unknown)") var errDecode = errors.New("cannot parse invalid wire-format data") + +var errRecursionDepth = errors.New("exceeded maximum recursion depth") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2025-10-31 09:31:27.000000000 +0100 +++ new/vendor/modules.txt 2026-07-07 16:03:17.000000000 +0200 @@ -1,23 +1,23 @@ # github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f ## explicit github.com/knqyf263/go-rpm-version -# golang.org/x/net v0.46.0 -## explicit; go 1.24.0 +# golang.org/x/net v0.56.0 +## explicit; go 1.25.0 golang.org/x/net/context golang.org/x/net/html golang.org/x/net/html/atom -# golang.org/x/sync v0.17.0 -## explicit; go 1.24.0 +# golang.org/x/sync v0.21.0 +## explicit; go 1.25.0 golang.org/x/sync/errgroup -# golang.org/x/text v0.30.0 -## explicit; go 1.24.0 +# golang.org/x/text v0.39.0 +## explicit; go 1.25.0 golang.org/x/text/internal/format golang.org/x/text/internal/language golang.org/x/text/internal/language/compact golang.org/x/text/internal/tag golang.org/x/text/language golang.org/x/text/language/display -# google.golang.org/protobuf v1.36.10 +# google.golang.org/protobuf v1.36.11 ## explicit; go 1.23 google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire
