Skip to content

rdf: fix end pointer guard checking the wrong slice in snippet range parsing - #292

Merged
kzantow merged 1 commit into
spdx:mainfrom
arpitjain099:fix/rdf-snippet-endpointer-guard
Jul 23, 2026
Merged

rdf: fix end pointer guard checking the wrong slice in snippet range parsing#292
kzantow merged 1 commit into
spdx:mainfrom
arpitjain099:fix/rdf-snippet-endpointer-guard

Conversation

@arpitjain099

Copy link
Copy Markdown
Contributor

While reading through the RDF snippet parser I noticed the end pointer guard in setSnippetRangeFromNode checks the wrong slice:

endPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_END_POINTER, nil)
if len(startPointerTriples) != 1 {
    return fmt.Errorf("range object must be associated with exactly 1 endPointer, got %d", len(endPointerTriples))
}
endRangeType, end, err := parser.getPointerFromNode(endPointerTriples[0].Object, si)

It's pretty much self-evidencing: the error message says endPointer and formats len(endPointerTriples), but the condition reads startPointerTriples, which the block right above has already validated as exactly 1. So the guard can never fire, and endPointerTriples[0] on the next line indexes whatever's there, empty slice or not.

To actually reach it you need a range node that clears the earlier checks (exactly 3 associated triples, exactly 1 rdf:type, exactly 1 startPointer) while having no endPointer. A third triple with some other predicate does it:

<j.0:StartEndPointer>
    <j.0:startPointer>
        <j.0:ByteOffsetPointer>
            <j.0:reference rdf:resource="...#SPDXRef-DoapSource"/>
            <j.0:offset>310</j.0:offset>
        </j.0:ByteOffsetPointer>
    </j.0:startPointer>
    <rdfs:comment>range without an end pointer</rdfs:comment>
</j.0:StartEndPointer>

That panics with index out of range [0] with length 0 instead of returning the error it meant to. Since RDF input is generally untrusted, a malformed document takes down the caller rather than getting a parse error back.

The fix is the one-line change to test endPointerTriples. The same line is wrong in both v2_2 and v2_3, so both are here in one PR.

On the test side: TestCase 4 in each file is labelled "triples with 0 endPointer" but it was actually built with two endPointers and zero startPointers, so it tripped the startPointer guard and returned early. That's why the branch never got caught. I rewrote it to be a real 0-endPointer case, which reproduces the panic on main and passes with the fix. TestCase 3 still covers the missing-startPointer path it duplicated.

Verified with go test ./..., all green. Reverting just the two source lines makes the two updated cases panic, so the tests do cover the change.

The guard ahead of endPointerTriples[0] tests len(startPointerTriples)
instead, which the previous block has already validated, so it always
passes. Its own error message says endPointer, which is what it was
meant to check.

A range node with a valid startPointer and no endPointer clears every
earlier check (3 associated triples, exactly 1 rdf:type, exactly 1
startPointer) if the third triple is some other predicate, and then
indexes an empty slice. That panics the parser on malformed RDF input
rather than returning the intended error.

Same line was wrong in v2_2 and v2_3, so both are fixed here.

The existing 0-endPointer test case actually had two endPointers and no
startPointer, so it tripped the startPointer guard and never reached
this branch. Rewrote it to match what it says it covers.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>

@kzantow kzantow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@kzantow
kzantow merged commit bb84d64 into spdx:main Jul 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants