Skip to content
carabiner-devPublic

About

OpenEOX utilities and data types

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

OpenEoX Parser, Go Types and Protobuf definitions

This repository contains a parser and types to read and work with OASIS OpenEoX data. This repo also hosts the protocol buffer definitions of the OpenEoX elements from which the types are generated.

What is OpenEoX

From the official website:

OpenEoX is an initiative aimed at standardizing the way End-of-Life (EOL) and End-of-Support (EOS) information is exchanged within the software and hardware industries.

OpenEoX is a simple format to communicate the end of support of software and hardware. It is designed to complement SBOM, VEX and security advisories.

Install

To install the module, simply pull it with go get:

go get github.com/carabiner-dev/openeox

Usage

Parsing

The parser reads both OpenEoX Shell and Core documents:

parser, _ := openeox.NewParser()

// Parse a shell document (contains product info + lifecycle data)
shell, err := parser.ParseShell(data)

// Parse a standalone core document (lifecycle data only)
core, err := parser.ParseCore(data)

Core documents are validated against the upstream OpenEoX core JSON schema when parsed; validation errors wrap openeox.ErrInvalidDocument. Set Parser.SkipValidation to parse without validating.

Documents can also be read directly from the file system, a URL or any io.Reader:

core, err := parser.ParseCoreFile("product.eox.json")
core, err := parser.ParseCoreURL(ctx, "https://example.com/product.eox.json")
core, err := parser.ParseCoreReader(os.Stdin)
// ... and the equivalent ParseShellFile / ParseShellURL / ParseShellReader

Handling "tba" (To Be Announced)

The OpenEoX spec allows lifecycle date fields (end_of_life, end_of_security_support, end_of_sales, general_availability) to be the literal string "tba" instead of an RFC 3339 timestamp, meaning the date has not yet been announced.

Since the Go types use google.protobuf.Timestamp for these fields (giving you real time.Time values), the parser transparently maps "tba" to a far-future sentinel timestamp (9999-12-31T23:59:59Z). Use IsTBA to check whether a parsed timestamp represents "tba":

core, _ := parser.ParseCore(data)

if openeox.IsTBA(core.GetEndOfLife()) {
    fmt.Println("End of life has not been announced yet")
} else {
    fmt.Println("End of life:", core.GetEndOfLife().AsTime())
}

To create a TBA timestamp when building documents:

core := &openeox.Core{
    Schema:               openeox.CoreSchema,
    EndOfLife:            openeox.TBATimestamp(),
    EndOfSecuritySupport: openeox.TBATimestamp(),
    LastUpdated:          timestamppb.Now(),
}

The marshal functions (MarshalCore, MarshalShell) automatically convert sentinel timestamps back to "tba" in the JSON output, producing spec-compliant documents.

Building a Document

Here is a complete example that builds an OpenEoX shell document with product identification using file hashes:

shell := &openeox.Shell{
    Schema: openeox.Schema,
    Statements: []*openeox.Statement{
        {
            Core: &openeox.Core{
                Schema:               openeox.CoreSchema,
                EndOfLife:            timestamppb.New(time.Date(2028, 6, 30, 23, 59, 59, 0, time.UTC)),
                EndOfSecuritySupport: timestamppb.New(time.Date(2028, 3, 31, 23, 59, 59, 0, time.UTC)),
                EndOfSales:           timestamppb.New(time.Date(2027, 12, 31, 23, 59, 59, 0, time.UTC)),
                LastUpdated:          timestamppb.Now(),
            },
            Product: &openeox.Product{
                Schema:         openeox.ProductSoftwareSchema,
                ProductName:    "SecureLib",
                ProductVersion: "2.1.0",
                VendorName:     "Acme Corp",
            },
            ProductIdentificationHelper: &openeox.ProductIdentificationHelper{
                Purls: []string{"pkg:generic/acme/securelib@2.1.0"},
                Hashes: []*openeox.CryptographicHashes{
                    {
                        Filename: "securelib-2.1.0.tar.gz",
                        FileHashes: []*openeox.FileHash{
                            {
                                Algorithm: "sha256",
                                Value:     "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
                            },
                        },
                    },
                },
            },
        },
    },
}

data, err := openeox.MarshalShell(shell)

This produces:

{
  "$schema": "https://docs.oasis-open.org/openeox/tbd/schema/shell.json",
  "statements": [
    {
      "core": {
        "$schema": "https://docs.oasis-open.org/openeox/eox-core/v1.0/schema/core.json",
        "end_of_life": "2028-06-30T23:59:59Z",
        "end_of_security_support": "2028-03-31T23:59:59Z",
        "end_of_sales": "2027-12-31T23:59:59Z",
        "last_updated": "2025-04-13T12:00:00Z"
      },
      "product": {
        "$schema": "https://docs.oasis-open.org/openeox/tbd/schema/product_software.json",
        "product_name": "SecureLib",
        "product_version": "2.1.0",
        "vendor_name": "Acme Corp"
      },
      "product_identification_helper": {
        "purls": ["pkg:generic/acme/securelib@2.1.0"],
        "hashes": [
          {
            "filename": "securelib-2.1.0.tar.gz",
            "file_hashes": [
              {
                "algorithm": "sha256",
                "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
              }
            ]
          }
        ]
      }
    }
  ]
}

Marshaling

Use the provided marshal functions to produce JSON that conforms to the upstream OpenEoX schema. Object keys are sorted lexicographically, as required by the specification's Library conformance profile:

data, err := openeox.MarshalCore(core)
data, err := openeox.MarshalShell(shell)

// String, stream and file system output
s, err := openeox.MarshalCoreString(core)
err = openeox.WriteCore(os.Stdout, core)
err = openeox.WriteCoreFile("product.eox.json", core)
// ... and the equivalent MarshalShellString / WriteShell / WriteShellFile

Spec Conformance

The module tracks the OASIS OpenEoX specification:

  • openeox-core v1.0: Fully supported. The Core message maps directly to the upstream JSON schema. Conformance is validated against the upstream schema and test data in CI. Documents using the pre-CSD01-RC3 schema URI (https://docs.oasis-open.org/openeox/v1.0/schema/core.json, exposed as CoreSchemaLegacy) are still accepted when parsing. The module targets the specification's "OpenEoX Core Library" conformance profile (Conformance Clause 9): parsed core documents are checked against the embedded upstream JSON schema, documents can be read from the file system, URLs and data streams, output keys are sorted, and documents can be written to the file system, strings and data streams.
  • openeox-shell: Tracks the current draft (CSD01). The Shell, Statement, Product, and ProductIdentificationHelper messages follow the draft shell schema structure.

Copyright and License

This module is copyright by Carabiner Systems, Inc. It is released under the Apache 2.0 license, feel free to use it for anything, contribute patches or file issues with bugs or feature requests.

About

OpenEOX utilities and data types

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages