From 696f117c224c5062979c66ce22a5f336864e7ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Thu, 17 Sep 2026 23:04:49 +0200 Subject: [PATCH] fix(parser): reject an array-of-tables header extending a frozen inline table Assisted-by: GLM 5.3 --- CHANGELOG.md | 5 +++++ interpres_test.go | 3 +++ parser.go | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ecbcd..fa37668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- An array-of-tables header whose path runs through an inline table + (`a = {b = {}}` followed by `[[a.b.c]]`) is rejected. The frozen-inline-table + check covered `[table]` headers and dotted keys but not the intermediate + steps of an array-of-tables header, so such a document silently extended the + inline table. - A float with an exponent marker but no digits (`1e`, `0.0E`) is rejected; the exponent requires at least one digit. - A date-time offset outside 00:00 through 23:59 is rejected; such offsets diff --git a/interpres_test.go b/interpres_test.go index 7b3e3fa..dbcbcd8 100644 --- a/interpres_test.go +++ b/interpres_test.go @@ -487,6 +487,9 @@ func TestRejectsInlineTableExtension(t *testing.T) { "by header": "a = { b = 1 }\n[a.c]\nx = 2\n", "by dotted key": "a = { b = 1 }\na.c = 2\n", "header over it": "a = { b = 1 }\n[a]\nx = 2\n", + // The frozen check must cover the intermediate steps of an array-of-tables + // header, not only the leaf: [[a.b.c]] walks through a and a.b. + "by nested array header": "a = { b = {} }\n[[a.b.c]]\nx = 2\n", } for name, doc := range cases { if _, err := Parse([]byte(doc)); err == nil { diff --git a/parser.go b/parser.go index fb0715e..9037df9 100644 --- a/parser.go +++ b/parser.go @@ -171,7 +171,12 @@ func (p *parser) tableAt(key []string) (map[string]any, error) { func (p *parser) appendArrayTable(key []string) (map[string]any, error) { parent := p.root + path := make([]string, 0, len(key)) for _, k := range key[:len(key)-1] { + path = append(path, k) + if p.frozen[pathKey(path)] { + return nil, p.errf("cannot extend inline table %q", strings.Join(path, ".")) + } existing, ok := parent[k] if !ok { next := map[string]any{}