panic: nil-pointer dereference when parsing a dangling open parenthesis
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
Research direction
Start with spdxexp/parse.go, especially parseOperator, peek, and parseParenthesizedExpression, then reproduce the issue through ValidateAndNormalizeLicensesWithOptions using a trailing open parenthesis. Add regression coverage for the listed dangling-parenthesis inputs and verify they return invalid without panicking, while existing malformed and balanced inputs remain unchanged.
Written by the indexing model from the issue text.
Description
Summary
ValidateAndNormalizeLicensesWithOptions (and any other entry point that routes through the expression parser) panics with a nil-pointer dereference when the input contains a dangling open parenthesis, instead of reporting the string as invalid.
Affected version: v2.7.0 (latest).
Reproduction
package main
import (
"fmt"
"github.com/github/go-spdx/v2/spdxexp"
)
func main() {
// panics: runtime error: invalid memory address or nil pointer dereference
valid, invalid := spdxexp.ValidateAndNormalizeLicensesWithOptions(
[]string{"("}, spdxexp.ValidateLicensesOptions{})
fmt.Println(valid, invalid)
}
Any input with an unclosed trailing open paren reproduces it: "(", "((", "MIT OR (", "( ".
Balanced or otherwise-malformed inputs behave correctly and are returned as invalid (e.g. ")", "(MIT", "MIT)", "()", "(())").
Panic
panic: runtime error: invalid memory address or nil pointer dereference
spdxexp.(*tokenStream).parseOperator(...) spdxexp/parse.go:364
spdxexp.(*tokenStream).parseParenthesizedExpression(...) spdxexp/parse.go:97
spdxexp.(*tokenStream).parseAtom(...)
spdxexp.(*tokenStream).parseAnd(...)
spdxexp.(*tokenStream).parseExpression(...)
spdxexp.(*tokenStream).parseParenthesizedExpression(...) spdxexp/parse.go:103
...
Root cause
parseOperator dereferences the result of peek() without a nil check:
// spdxexp/parse.go:362
func (t *tokenStream) parseOperator(operator string) *string {
token := t.peek() // returns nil at end-of-stream
if token.role == operatorToken && token.value == operator { // line 364: nil deref
...
peek() returns nil when the stream is exhausted:
// spdxexp/parse.go:79
func (t *tokenStream) peek() *token {
if t.hasMore() {
token := t.tokens[t.index]
return &token
}
return nil
}
parseParenthesizedExpression consumes the ( and then recurses into parseExpression without first checking hasMore():
// spdxexp/parse.go:96
func (t *tokenStream) parseParenthesizedExpression() *node {
openParen := t.parseOperator("(")
if openParen == nil {
return nil
}
expr := t.parseExpression() // line 103: recurses even when no tokens remain
...
So for a trailing (, the recursion bottoms out in parseParenthesizedExpression → parseOperator("(") against an empty stream, peek() returns nil, and line 364 dereferences it.
Expected behavior
A dangling open paren is a syntax error; it should be reported via the invalid return (as "(MIT" and "()" already are), not panic. Callers validating untrusted license strings currently have to wrap every call in recover().
Possible fixes
- Guard
parseOperator(and any other unguardedpeek()caller) against aniltoken, or - Have
parseParenthesizedExpressioncheckt.hasMore()before recursing intoparseExpression.
- Dominant language
- Go
- Stars
- 53
- Forks
- 16
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from github/go-spdx
-
Difficulty 3/5 1-2 days Newbie friendliness 65/100
-
Difficulty 3/5 1-2 days Newbie friendliness 38/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
maintenance
Difficulty 1/5 1-3 hours Newbie friendliness 45/100
-
documentation
Difficulty 5/5 Over a week Newbie friendliness 25/100
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 84/100
-
enhancement needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
kind/cleanup
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
kubernetes-sigs/kueue#15947 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
sympozium-ai/sympozium#627 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100