CVE-2022-28946 (published 2022-05-19T19:15Z)

An issue in the component ast/parser.go of Open Policy Agent v0.39.0 causes the application to incorrectly interpret every expression, causing a Denial of Service (DoS) via triggering out-of-range memory access.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/open-policy-agent/opa
commit_details: repo_url: https://github.com/open-policy-agent/opa
author: Stephan Renatus
author_date: 2022-04-05 13:17:11+02:00
msg: ast/parser: guard against invalid domains for "some" and "every" (#4548)

These would cause a runtime exception when attempting to parse

    some internal.member_2()

This is due to a technicality in how

    some x in xs

is parsed: `x in xs` will first become `internal.member_2(x, xs)`,
and `some internal.member_2(x, xs)` is then further processed. The
assumption that there're always two (resp. three for internal.member_3)
arguments won't hold if a snippet like the one above is fed into
the parser.

Thanks to Norbert Szetei of Doyensec, @doyensec, for reporting this.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
repository: repo_url: https://github.com/open-policy-agent/opa
repo_name: open-policy-agent/opa
description: Open Policy Agent (OPA) is an open source, general-purpose policy engine.
repo_language: Go
owner: open-policy-agent
file_changes: filename: parser.go
change_type: ModificationType.MODIFY
diff: @@ -897,7 +897,16 @@ func (p *Parser) parseSome() *Expr {
 	if term := p.parseTermInfixCall(); term != nil {
 		if call, ok := term.Value.(Call); ok {
 			switch call[0].String() {
-			case Member.Name, MemberWithKey.Name: // OK
+			case Member.Name:
+				if len(call) != 3 {
+					p.illegal("illegal domain")
+					return nil
+				}
+			case MemberWithKey.Name:
+				if len(call) != 4 {
+					p.illegal("illegal domain")
+					return nil
+				}
 			default:
 				p.illegal("expected `x in xs` or `x, y in xs` expression")
 				return nil
@@ -972,9 +981,17 @@ func (p *Parser) parseEvery() *Expr {
 	}
 	switch call[0].String() {
 	case Member.Name: // x in xs
+		if len(call) != 3 {
+			p.illegal("illegal domain")
+			return nil
+		}
 		qb.Value = call[1]
 		qb.Domain = call[2]
 	case MemberWithKey.Name: // k, v in xs
+		if len(call) != 4 {
+			p.illegal("illegal domain")
+			return nil
+		}
 		qb.Key = call[1]
 		qb.Value = call[2]
 		qb.Domain = call[3]

programming_language: Go
filename: parser_test.go
change_type: ModificationType.MODIFY
diff: @@ -785,6 +785,10 @@ func TestSomeDeclExpr(t *testing.T) {
 			},
 			With: []*With{{Value: ArrayTerm(), Target: NewTerm(MustParseRef("input"))}},
 		}, opts)
+
+	assertParseErrorContains(t, "invalid domain (internal.member_2)", "some internal.member_2()", "illegal domain", opts)
+	assertParseErrorContains(t, "invalid domain (internal.member_3)", "some internal.member_3()", "illegal domain", opts)
+
 }
 
 func TestEvery(t *testing.T) {
@@ -851,6 +855,8 @@ func TestEvery(t *testing.T) {
 		"unexpected ident token: expected \\n or ; or }\n\tevery x\n", // this asserts that the tail of the error message doesn't contain a hint
 	)
 
+	assertParseErrorContains(t, "invalid domain (internal.member_2)", "every internal.member_2()", "illegal domain", opts)
+	assertParseErrorContains(t, "invalid domain (internal.member_3)", "every internal.member_3()", "illegal domain", opts)
 }
 
 func TestNestedExpressions(t *testing.T) {

programming_language: Go