Skip to content

Commit 4c59ece

Browse files
committed
Catch open parenthesis with no following tokens
prevents panic in cases where an open parenthesis is the last token in the stream
1 parent 0e6bf86 commit 4c59ece

3 files changed

Lines changed: 37 additions & 1 deletion

File tree

‎spdxexp/parse.go‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,26 @@ func (t *tokenStream) parseParenthesizedExpression() *node {
9999
// paren not found
100100
return nil
101101
}
102+
if !t.hasMore() {
103+
// catch the case where there are no tokens after an open parenthesis
104+
t.err = errors.New("open parenthesis does not have a matching close parenthesis")
105+
return nil
106+
}
102107

103108
expr := t.parseExpression()
104109
if t.err != nil {
105110
return nil
106111
}
107112

108113
if !t.hasMore() {
109-
// no more tokens, so missing closing paren
114+
// catch the case where there are no tokens after processing expression starting with an open parenthesis
110115
t.err = errors.New("open parenthesis does not have a matching close parenthesis")
111116
return nil
112117
}
113118

114119
closeParen := t.parseOperator(")")
115120
if closeParen == nil {
121+
// catch the case where the next token is not the expected close parenthesis
116122
t.err = errors.New("open parenthesis does not have a matching close parenthesis")
117123
return nil
118124
}
@@ -281,6 +287,9 @@ func (t *tokenStream) parseLicenseRef() *node {
281287
ref := referenceNodePartial{documentRef: "", hasDocumentRef: false, licenseRef: ""}
282288

283289
token := t.peek()
290+
if token == nil {
291+
return nil
292+
}
284293
if token.role == documentRefToken {
285294
ref.documentRef = token.value
286295
ref.hasDocumentRef = true
@@ -294,6 +303,12 @@ func (t *tokenStream) parseLicenseRef() *node {
294303
}
295304

296305
token = t.peek()
306+
if token == nil {
307+
if ref.hasDocumentRef {
308+
t.err = errors.New("expected 'LicenseRef-...' after 'DocumentRef-...'")
309+
}
310+
return nil
311+
}
297312
if token.role != licenseRefToken && ref.hasDocumentRef {
298313
t.err = errors.New("expected 'LicenseRef-...' after 'DocumentRef-...'")
299314
return nil
@@ -315,6 +330,9 @@ func (t *tokenStream) parseLicenseRef() *node {
315330
// an error is returned. Advances the index if a valid license is found.
316331
func (t *tokenStream) parseLicense() *node {
317332
token := t.peek()
333+
if token == nil {
334+
return nil
335+
}
318336
if token.role != licenseToken {
319337
return nil
320338
}
@@ -361,6 +379,9 @@ func (t *tokenStream) parseLicense() *node {
361379
// Advances the index if the operator is found.
362380
func (t *tokenStream) parseOperator(operator string) *string {
363381
token := t.peek()
382+
if token == nil {
383+
return nil
384+
}
364385
if token.role == operatorToken && token.value == operator {
365386
t.next()
366387
return &(token.value)

‎spdxexp/parse_test.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func TestParse(t *testing.T) {
4141
"MIT", nil},
4242

4343
{"empty expression", "", nil, "", errors.New("parse error - cannot parse empty string")},
44+
{"operator error - nothing after open parenthesis", "(", nil, "", errors.New("open parenthesis does not have a matching close parenthesis")},
45+
{"operator error - nested dangling open parenthesis", "((", nil, "", errors.New("open parenthesis does not have a matching close parenthesis")},
46+
{"operator error - expression ending in open parenthesis", "MIT OR (", nil, "", errors.New("open parenthesis does not have a matching close parenthesis")},
47+
{"document reference without license reference", "DocumentRef-spdx-tool-1.2:", nil, "", errors.New("expected 'LicenseRef-...' after 'DocumentRef-...'")},
4448

4549
{"invalid license", "NON-EXISTENT-LICENSE", nil, "",
4650
errors.New("unknown license 'NON-EXISTENT-LICENSE' at offset 0")},
@@ -1075,6 +1079,7 @@ func TestParseTokens(t *testing.T) {
10751079
},
10761080
"{ LEFT: { LEFT: MIT and RIGHT: Apache-1.0+ } or RIGHT: { LEFT: DocumentRef-spdx-tool-1.2:LicenseRef-MIT-Style-2 or RIGHT: GPL-2.0 with Bison-exception-2.2 } }", nil,
10771081
},
1082+
10781083
{"operator error - missing close parenthesis", getMissingEndParenTokens(0),
10791084
&node{}, "", errors.New("open parenthesis does not have a matching close parenthesis"),
10801085
},
@@ -1214,6 +1219,7 @@ func TestParseOperator(t *testing.T) {
12141219
{"looking for + operator", getPlusClauseTokens(1), "+", false, 2},
12151220
{"looking for OR operator, but got AND", getAndClauseTokens(1), "OR", true, 1},
12161221
{"looking for OR operator, but got LICENSE", getOrClauseTokens(0), "OR", true, 0},
1222+
{"looking for operator past end", getAndClauseTokens(3), "OR", true, 3},
12171223
}
12181224

12191225
for _, test := range tests {

‎spdxexp/satisfies_test.go‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,15 @@ func TestValidateAndNormalizeLicensesWithOptions_FailComplexExpressions(t *testi
272272
}
273273
}
274274

275+
func TestValidateAndNormalizeLicensesWithOptions_MalformedTrailingTokens(t *testing.T) {
276+
licenses := []string{"(", "((", "MIT OR (", "DocumentRef-spdx-tool-1.2:"}
277+
278+
normalizedLicenses, invalidLicenses := ValidateAndNormalizeLicensesWithOptions(licenses, ValidateLicensesOptions{})
279+
280+
assert.Empty(t, normalizedLicenses)
281+
assert.Equal(t, licenses, invalidLicenses)
282+
}
283+
275284
func TestValidateAndNormalizeLicensesWithOptions_FailDeprecatedLicenses(t *testing.T) {
276285
// eCos-2.0 is a known deprecated SPDX license ID (see TestDeprecatedLicense).
277286
deprecatedLicense := "eCos-2.0"

0 commit comments

Comments
 (0)