Validating STIX 2.1: Threat Intelligence in a Form Machines Can Share

Threat intelligence is only useful if it can be shared accurately between organisations and tools. A report that one analyst understands perfectly is of little use if another organisation's system cannot parse it, misreads its dates, or follows a reference to an object that does not exist. STIX, short for Structured Threat Information Expression, is the standard language for sharing threat intelligence in structured form, and version 2.1 is the current widely used version. A STIX bundle describes things like threat actors, malware, indicators, attack patterns, and the relationships between them, in a JSON format that tools can exchange automatically. The STIX 2.1 Validator checks a bundle you paste in, testing whether its objects follow the standard, so sharing failures are caught before the data leaves your hands.

A STIX bundle is a collection of objects, each with a type, a unique identifier, creation and modification timestamps, and type-specific properties. An identity object names an organisation or person. A malware object describes a malicious program. An indicator object describes a pattern, such as a file hash or an address, that signifies malicious activity. An attack pattern object describes a technique from a known catalogue. A relationship object connects two other objects, saying, for example, that a particular malware uses a particular attack pattern, or that an indicator indicates a particular malware. This object-and-relationship structure is what makes STIX powerful: it is not a flat list of bad addresses, but a small graph of who did what, with what, and how it was observed.

How it works

Validation happens in layers similar to those for other structured formats. The first layer is JSON syntax: the bundle must be well-formed JSON, and a single misplaced bracket invalidates everything. The second layer is object structure: each object must carry the properties its type requires, such as a name for an identity, a pattern for an indicator, and a relationship type plus two references for a relationship. Timestamps must be in the required format, and identifiers must follow the required pattern of type plus a unique value. The third layer is vocabulary: fields that draw from defined lists, such as malware types, indicator types, and relationship types, must use values from those lists, because a misspelled type will be ignored or rejected by a receiving tool even though a human reader understands it.

The fourth layer is referential integrity: every relationship must point at objects that actually exist in the bundle, and every reference, such as an indicator's reference to a malware object, must resolve. This layer catches the most damaging sharing errors, because a bundle that references a missing object tells the receiver a story with a character who never appears. The validator also checks bundle-level rules, such as that the bundle object itself is correctly formed and that object identifiers are unique within the bundle, because duplicate identifiers make it impossible to tell which version of an object is meant. As with other validators, passing validation proves the bundle is well-formed and shareable, not that its intelligence is accurate, which remains a matter of analyst judgement and source reliability.

Worked example

Consider a small sample bundle with four objects, using sample values for illustration. An identity object names Example Research Team. A malware object names ExampleLocker, with a type of ransomware. An indicator object carries a file hash pattern for a sample of ExampleLocker. A relationship object states that the indicator indicates ExampleLocker. The validator checks each object: the identity has a name, the malware has a name and a valid type, the indicator has a pattern, and the relationship has a valid type and two references. It then checks the references: the relationship's source reference points at the indicator, and its target reference points at the malware, both of which exist in the bundle. The bundle validates cleanly, and a receiving tool can now import it and answer the question it exists to answer: this indicator is associated with this ransomware, as reported by this team.

Now suppose the malware object is renamed during editing, changing its identifier, but the relationship is not updated. The validator reports a dangling reference: the relationship points at an identifier that no longer exists. A human reading the bundle might guess the intended target, but a receiving tool cannot, and it will either reject the bundle or import a broken graph. The fix is to update the relationship to the new identifier, or to restore the original identifier, and then re-validate. That single check, repeated across a large bundle with hundreds of objects, is what prevents a sharing exchange from silently degrading into a collection of disconnected fragments. These are sample values for illustration, and the pattern they show, validate before sharing, applies to every bundle regardless of size.

Mistakes to avoid

The most common mistake is writing STIX by hand in a text editor, which invites syntax errors, misspelled types, and dangling references that a purpose-built tool would prevent. Generate STIX from a tool or a library where possible, and validate even generated output, because generators have bugs too. A second mistake is using free-text labels where the standard requires a defined value, such as inventing a malware type instead of choosing from the list. A third mistake is neglecting timestamps: creation and modification times must be correctly formatted and sensible, with modification never earlier than creation, because receiving tools sort and deduplicate by these times, and a bundle with confused timestamps will be processed in the wrong order.

A fourth mistake is sharing a bundle without checking its references, which produces the dangling-reference problem from the example at scale. A fifth mistake is treating validation as verification of truth. A bundle can validate perfectly while describing an event that never happened, a misattributed actor, or an indicator that is actually benign, because the validator checks form, not fact. Source reliability, corroboration, and analyst review remain essential, and a validated bundle should still carry its confidence and sourcing information honestly. Finally, the handling rule: threat intelligence can itself be sensitive, especially when it describes ongoing incidents, internal systems, or unpatched weaknesses. Do not paste live incident data, internal addresses, or credentials into an external validator; validate with sample or sanitised bundles, and share real intelligence only through the channels your organisation approves.

Putting it into practice

Validate every bundle before sharing it, whether the sharing is with a partner, a community, or another tool in your own pipeline, and keep the validated bundle alongside the sharing record so the exact data shared can be reconstructed later. When a bundle fails, fix the findings in the bundle source, not by editing the shared copy by hand, or the source and the shared copy will diverge. For neighbouring tools, the XML Formatter is useful when converting or inspecting older XML-based threat formats, and the Threat Intelligence Report Builder helps organise the reporting that STIX bundles formalise. For background reading, the related guide explains structured format hygiene generally, which is the same discipline STIX validation applies to threat data.

Key points

  • STIX 2.1 expresses threat intelligence as objects and relationships in JSON bundles
  • Validation checks syntax, required properties, defined vocabularies, and reference integrity
  • A dangling reference breaks the graph a receiver tries to import, even if each object looks fine
  • Validation proves a bundle is shareable, not that its intelligence is accurate