Skip to content

Commit

Permalink
mod/modfile: parse module.cue in data-only mode
Browse files Browse the repository at this point in the history
This reduces the surface for denial of service attacks involving
arbitrary computations via the module.cue file.

Note that we never supported non-data CUE in cue.mod/module.cue;
commands like `cue mod tidy` and `cue mod get` assumed plain CUE data so
that they could modify some fields and write the file back to disk.

Fixes #3138.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: If58c8d3a7b05a657543909bf9d1ebaaf4296d039
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194669
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
  • Loading branch information
rogpeppe committed May 15, 2024
1 parent a861376 commit 21a5c8b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 6 deletions.
36 changes: 30 additions & 6 deletions mod/modfile/modfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ import (

"cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal/cueversion"
"cuelang.org/go/internal/encoding"
"cuelang.org/go/internal/filetypes"
"cuelang.org/go/mod/module"
)

Expand Down Expand Up @@ -230,8 +232,13 @@ func Parse(modfile []byte, filename string) (*File, error) {
// that only supports the single field "module" and ignores all other
// fields.
func ParseLegacy(modfile []byte, filename string) (*File, error) {
ctx := cuecontext.New()
file, err := parseDataOnlyCUE(ctx, modfile, filename)
if err != nil {
return nil, errors.Wrapf(err, token.NoPos, "invalid module.cue file syntax")
}
// Unfortunately we need a new context. See the note inside [moduleSchemaDo].
v := cuecontext.New().CompileBytes(modfile, cue.Filename(filename))
v := ctx.BuildFile(file)
if err := v.Err(); err != nil {
return nil, errors.Wrapf(err, token.NoPos, "invalid module.cue file")
}
Expand All @@ -255,14 +262,14 @@ func ParseNonStrict(modfile []byte, filename string) (*File, error) {
}

func parse(modfile []byte, filename string, strict bool) (*File, error) {
file, err := parser.ParseFile(filename, modfile)
// Unfortunately we need a new context. See the note inside [moduleSchemaDo].
ctx := cuecontext.New()
file, err := parseDataOnlyCUE(ctx, modfile, filename)
if err != nil {
return nil, errors.Wrapf(err, token.NoPos, "invalid module.cue file syntax")
}
// TODO disallow non-data-mode CUE.

// Unfortunate to need a new context, but see the note inside [moduleSchemaDo].
v := cuecontext.New().BuildFile(file)
v := ctx.BuildFile(file)
if err := v.Validate(cue.Concrete(true)); err != nil {
return nil, errors.Wrapf(err, token.NoPos, "invalid module.cue file value")
}
Expand Down Expand Up @@ -389,6 +396,23 @@ func parse(modfile []byte, filename string, strict bool) (*File, error) {
return mf, nil
}

func parseDataOnlyCUE(ctx *cue.Context, cueData []byte, filename string) (*ast.File, error) {
dec := encoding.NewDecoder(ctx, &build.File{
Filename: filename,
Encoding: build.CUE,
Interpretation: build.Auto,
Form: build.Data,
Source: cueData,
}, &encoding.Config{
Mode: filetypes.Export,
AllErrors: true,
})
if err := dec.Err(); err != nil {
return nil, err
}
return dec.File(), nil
}

func newCUEError(err error, filename string) error {
ps := errors.Positions(err)
for _, p := range ps {
Expand Down
41 changes: 41 additions & 0 deletions mod/modfile/modfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,47 @@ cue: lang: "xxx"
want: &File{
Module: "foo.com/bar",
},
}, {
testName: "LegacyReferencesNotAllowed",
parse: ParseLegacy,
data: `
module: _foo
_foo: "blah.example"
`,
wantError: `invalid module.cue file syntax: references not allowed in data mode:
module.cue:2:9`,
}, {
testName: "ReferencesNotAllowed#1",
parse: Parse,
data: `
module: "foo.com/bar"
_foo: "v0.9.0"
cue: lang: _foo
`,
wantError: `invalid module.cue file syntax: references not allowed in data mode:
module.cue:4:12`,
}, {
testName: "ReferencesNotAllowed#2",
parse: Parse,
data: `
module: "foo.com/bar"
let foo = "v0.9.0"
cue: lang: foo
`,
wantError: `invalid module.cue file syntax: references not allowed in data mode:
module.cue:3:1
invalid module.cue file syntax: references not allowed in data mode:
module.cue:4:12`,
}, {
testName: "DefinitionsNotAllowed",
parse: Parse,
data: `
module: "foo.com/bar"
#x: "v0.9.0"
cue: lang: "v0.9.0"
`,
wantError: `invalid module.cue file syntax: definitions not allowed in data mode:
module.cue:3:1`,
}}

func TestParse(t *testing.T) {
Expand Down

0 comments on commit 21a5c8b

Please sign in to comment.