armor: reject empty lines in armored data

Caught by the new CCTV test vectors!
This commit is contained in:
Filippo Valsorda
2025-12-07 21:15:38 +01:00
committed by Filippo Valsorda
parent d7409cdc74
commit 96b6476140
4 changed files with 27 additions and 3 deletions

View File

@@ -140,6 +140,9 @@ func (r *armoredReader) Read(p []byte) (int, error) {
if string(line) == Footer { if string(line) == Footer {
return 0, r.setErr(drainTrailing()) return 0, r.setErr(drainTrailing())
} }
if len(line) == 0 {
return 0, r.setErr(errors.New("empty line in armored data"))
}
if len(line) > format.ColumnsPerLine { if len(line) > format.ColumnsPerLine {
return 0, r.setErr(errors.New("column limit exceeded")) return 0, r.setErr(errors.New("column limit exceeded"))
} }

2
go.mod
View File

@@ -13,7 +13,7 @@ require (
// Test dependencies. // Test dependencies.
require ( require (
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 c2sp.org/CCTV/age v0.0.0-20250426113718-46fad5b26cb2
github.com/rogpeppe/go-internal v1.12.0 github.com/rogpeppe/go-internal v1.12.0
golang.org/x/tools v0.22.0 // indirect golang.org/x/tools v0.22.0 // indirect
) )

4
go.sum
View File

@@ -1,5 +1,5 @@
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20250426113718-46fad5b26cb2 h1:CgfUtBNKpcGa3dLCktwniIKTMkxlELJcvS+EQRlGeGs=
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= c2sp.org/CCTV/age v0.0.0-20250426113718-46fad5b26cb2/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A= filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A=

View File

@@ -9,6 +9,7 @@ package age_test
import ( import (
"bytes" "bytes"
"compress/zlib"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"errors" "errors"
@@ -55,6 +56,7 @@ type vector struct {
} }
func parseVector(t *testing.T, test []byte) *vector { func parseVector(t *testing.T, test []byte) *vector {
var z bool
v := &vector{file: test} v := &vector{file: test}
for { for {
line, rest, ok := bytes.Cut(v.file, []byte("\n")) line, rest, ok := bytes.Cut(v.file, []byte("\n"))
@@ -105,12 +107,31 @@ func parseVector(t *testing.T, test []byte) *vector {
v.identities = append(v.identities, i) v.identities = append(v.identities, i)
case "armored": case "armored":
v.armored = true v.armored = true
case "compressed":
if value != "zlib" {
t.Fatal("invalid test file: unknown compression:", value)
}
z = true
case "comment": case "comment":
t.Log(value) t.Log(value)
default: default:
t.Fatal("invalid test file: unknown header key:", key) t.Fatal("invalid test file: unknown header key:", key)
} }
} }
if z {
r, err := zlib.NewReader(bytes.NewReader(v.file))
if err != nil {
t.Fatal(err)
}
b, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if err := r.Close(); err != nil {
t.Fatal(err)
}
v.file = b
}
return v return v
} }