class

Vex::Document

Inherits JSON::Serializable < Reference < Object

Constructors

from_file(path : String) : Document

Reads a VEX document from disk. Strips a leading UTF-8 BOM if present — Windows-emitted JSON often carries one, and Crystal's JSON parser is strict about leading whitespace/markers.

Source
merge(docs : Enumerable(Document), id : String = "", author : String = DEFAULT_AUTHOR, role : String | Nil = nil, timestamp : Time | Nil = Time.utc, tooling : String | Nil = nil) : Document

Combines multiple documents into one, preserving each statement's source order across the inputs. Value-equal statements are deduplicated — useful when feeds overlap on identical assertions. Per the spec, statements are not collapsed by (product, vuln): the resulting document carries the full history, and effective_statement selects the most recent ruling at lookup time.

The result inherits no metadata from the inputs by default; pass id:, author:, etc. to set them explicitly. When id: is empty, an @id is generated canonically from the merged statements.

Source
new(id : String, author : String = DEFAULT_AUTHOR, statements : Array(Statement) = [] of Statement, version : Int32 = 1, context : String = CONTEXT, timestamp : Time | Nil = Time.utc, last_updated : Time | Nil = nil, role : String | Nil = nil, tooling : String | Nil = nil)
Source
new(*, __pull_for_json_serializable pull : JSON::PullParser)
Source

Class methods

generate_canonical_id(statements : Array(Statement)) : String

Generates a deterministic IRI for a set of statements. The output is stable across statement order so two callers assembling the same data in different order receive the same @id — the property that lets consumers de-duplicate documents.

The hash covers each statement's vulnerability name (and id+aliases if present), status, justification, action_statement, and the recursive set of product/subcomponent identifiers. Mutable metadata like last_updated, status_notes, and statement-level timestamps is excluded so equivalent updates don't churn the document ID.

Source

Instance methods

==(other : self)

Returns true if this reference is the same as other. Invokes same?.

add_statement(statement : Statement) : self
Source
author
Source
author=(author : String)
Source
context
Source
context=(context : String)
Source
effective_products_for(stmt : Statement) : Array(Product) | Nil

Returns the effective products for a statement. Standalone OpenVEX has no encapsulating document, so this is just the statement's own products field — exposed as a helper for symmetry with effective_timestamp_for and so callers don't have to remember the inheritance semantics when wiring documents together.

Source
effective_statement(product : String, vulnerability : String) : Statement | Nil

Returns the most recent statement for the given product/vuln identifier pair. Compares timestamps with the document timestamp as fallback. Ties resolve to the last statement in source order — matching go-vex, which stable-sorts ascending then iterates from the end. This honors the conceptual model that newer statements (those appended later) override older ones when their timestamps cannot.

Source
effective_timestamp_for(stmt : Statement) : Time | Nil

Returns the effective timestamp for a statement, following the spec's inheritance flow: a statement-level timestamp wins, otherwise the document-level timestamp is inherited.

Source
find_statements(product : String, vulnerability : String) : Array(Statement)

Returns all statements that mention the given (product, vuln) pair, in source order. Useful for audit trails — "show me the full history of how this product was assessed against this CVE." For the single most recent ruling, use effective_statement.

Source
hash(hasher)

See Object#hash(hasher)

id=(id : String)
Source
last_updated
Source
last_updated=(last_updated : Time | Nil)
Source
merge(other : Document) : Document

Convenience: merge another document into a new document, keeping this one's identity (id, author, role, tooling). The receiver's statements come first so source order reflects "I had these, then I learned those." last_updated is bumped to now to signal the change.

Source
regenerate_id

Recomputes and sets @id from the current statements. Useful after building a document via add_statement if you didn't supply an @id. The new value is returned for chaining.

Source
role
Source
role=(role : String | Nil)
Source
statements
Source
statements=(statements : Array(Statement))
Source
timestamp
Source
timestamp=(timestamp : Time | Nil)
Source
to_json_pretty
Source
tooling
Source
tooling=(tooling : String | Nil)
Source
valid?
Source
validate

Returns validation errors for the document. The document is valid if every statement is valid and required document-level fields are present.

Source
version

version defaults to 0 as an "unset" sentinel so we can permissively parse documents that omit it. The spec requires version >= 1, so a sentinel 0 is never serialized — emitting "version": 0 would produce output the spec (and our own validate) rejects. The gap is still surfaced through validate.

Source
version=(version : Int32)

version defaults to 0 as an "unset" sentinel so we can permissively parse documents that omit it. The spec requires version >= 1, so a sentinel 0 is never serialized — emitting "version": 0 would produce output the spec (and our own validate) rejects. The gap is still surfaced through validate.

Source
warnings

Returns non-fatal spec advisories — issues the spec marks as SHOULD rather than MUST. Currently covers:

  • @context URLs that don't match the openvex.dev namespace
  • hash algorithms outside Appendix A
  • identifier types outside Appendix B A document with warnings is still valid? true; tools that want a stricter posture can fail on warnings.any? as their own policy.
Source
write(path : String) : Nil
Source