cwe.cr
cwe.cr
A Crystal implementation of the MITRE CWE (Common Weakness Enumeration). The full CWE Research view (view 1000) is embedded at compile time, so lookups, search, and relationship traversal need no network access or sidecar data files.
require "cwe"
w = CWE.find!("CWE-79")
w.name # => "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
w.abstraction # => CWE::Abstraction::Base
w.status # => CWE::Status::Stable
w.url # => "https://cwe.mitre.org/data/definitions/79.html"
w.common_consequences.first.scope # => "Confidentiality"
w.potential_mitigations.size # => 12
w.parent_relations.map(&.cwe_id) # => [74, 74]
Installation
Add to your shard.yml:
dependencies:
cwe:
github: hahwul/cwe.cr
Then shards install.
Usage
Lookups
CWE.find!("CWE-79") # raises CWE::NotFoundError if missing
CWE.find(79) # returns nil if missing
CWE[79] # bang variant, same as find!
CWE[79]? # nil variant, same as find
CWE.includes?("CWE-79") # => true
Any of these forms is accepted as an id: 79, "79", "CWE-79", "cwe-79", "CWE_79", "CWE:79". Whitespace around the value is tolerated.
Entry fields
Each CWE::Weakness exposes:
| field | type |
|---|---|
id, cwe_id, url |
Int32, "CWE-79", https://... |
name |
String |
abstraction |
CWE::Abstraction (Pillar, Class, Base, Variant, Compound) |
structure |
CWE::Structure (Simple, Composite, Chain) |
status |
CWE::Status (Stable, Draft, Incomplete, Deprecated, …) |
description, extended_description |
String? |
likelihood_of_exploit |
String? |
related_weaknesses |
Array(CWE::Related) — ChildOf / ParentOf / PeerOf / CanPrecede / CanFollow |
common_consequences |
Array(CWE::Consequence) — scope + impact + note |
potential_mitigations |
Array(CWE::Mitigation) — phase + strategy + description |
detection_methods |
Array(CWE::DetectionMethod) |
observed_examples |
Array(CWE::ObservedExample) — CVE references |
demonstrative_examples |
Array(CWE::DemonstrativeExample) — code samples (Bad/Good/Attack) |
applicable_platforms |
Array(CWE::ApplicablePlatform) |
alternate_terms |
Array(CWE::AlternateTerm) |
modes_of_introduction |
Array(CWE::ModeOfIntroduction) |
taxonomy_mappings |
Array(CWE::TaxonomyMapping) — PLOVER, OWASP, CAPEC, … |
related_attack_patterns |
Array(Int32) — CAPEC ids |
references |
Array(CWE::ReferenceLink) — REF-N citations |
mapping_notes |
CWE::MappingNotes? — usage policy + rationale |
content_history |
CWE::ContentHistory? — submission / last-modification dates |
notes |
Array(CWE::Note) |
Convenience helpers:
w = CWE.find!("CWE-79")
w.mapping_usage # => CWE::MappingUsage::Allowed
w.mappable? # => true
w.compound? # => false (Simple structure)
w.deprecated? # => false
Mapping policy
CWE 4.x assigns each entry a mapping Usage so tooling knows whether the entry is an acceptable target for a CVE / finding (Allowed, Allowed-with-Review, Discouraged, Prohibited). Categories and Views are always Prohibited.
CWE.find!(79).mapping_usage # => CWE::MappingUsage::Allowed
CWE.find!(20).mapping_usage # => CWE::MappingUsage::Discouraged (Class-level)
CWE.category!(227).mapping_usage # => CWE::MappingUsage::Prohibited
Demonstrative examples
w = CWE.find!(89) # SQL Injection
ex = w.demonstrative_examples.first
ex.intro_text # prose intro
ex.example_code.first.language # => "Java"
ex.example_code.first.nature # => "Bad"
ex.example_code.first.code # the snippet
External references
CWE stores all citations once in a catalog-level registry; individual entries link to them by id.
w = CWE.find!(79)
link = w.references.first # => CWE::ReferenceLink(@external_reference_id="REF-709", …)
ref = CWE.external_reference!(link.external_reference_id)
ref.title # => "OWASP …"
ref.url # => "https://…"
CWE.external_references.size # => 1000+
Walking the hierarchy
CWE.parents_of(79) # => [CWE-74]
CWE.children_of(79) # => [CWE-80, CWE-81, CWE-83, CWE-84, CWE-85, CWE-86, CWE-87]
CWE.ancestors_of(79) # nearest-first transitive closure
CWE.descendants_of(79)
CWE.pillar_of(79) # => CWE-707
# All edges are O(1) lookups via a pre-built index. Pass view_id to restrict
# to a particular CWE view (1000 = Research, 1003 = Simplified Mapping):
CWE.parents_of(79, view_id: 1000)
Search
CWE.search_by_name("cross-site scripting") # name-only match
CWE.search("HttpOnly") # name + description + alternate terms
CWE.with_abstraction(CWE::Abstraction::Pillar)
CWE.with_status(CWE::Status::Stable)
JSON
require "json"
CWE.find!(79).to_json
# {
# "id": 79,
# "cweId": "CWE-79",
# "name": "Improper Neutralization of Input During Web Page Generation (...)",
# "abstraction": "Base",
# "status": "Stable",
# "commonConsequences": [...],
# "potentialMitigations": [...],
# ...
# }
Categories and Views
CWE includes the full MITRE catalog — not just Weaknesses, but also Categories (informal groupings, "Mapping Prohibited") and Views (slices of the catalog organised around a stakeholder's perspective).
CWE.category!(227) # => CWE::Category: "7PK - API Abuse" (10 members)
CWE.view!(1000) # => CWE::View: "Research Concepts" (Graph)
CWE.members_of(1000) # => Array(CWE::Weakness) — the resolved members
# Unified lookup when you don't know which kind of entity an id refers to:
CWE.entry(79) # => Weakness
CWE.entry(227) # => Category
CWE.entry(1000) # => View
CWE.entry(99999) # => nil
Catalog metadata
CWE.catalog_version # => "4.20"
CWE.size # => 944 (weaknesses)
CWE.categories.size # => 422
CWE.views.size # => 59
CWE.external_references.size # => 1026
Examples
Runnable scripts under examples/:
basic.cr— lookups & id parsingdetails.cr— consequences, mitigations, CVEsrelationships.cr— parent/child traversalsearch.cr— full-text & filtered queriesjson_output.cr— JSON serialization
crystal run examples/basic.cr
Development
Run the test suite:
crystal spec
Regenerate the embedded data blob from a fresh MITRE CWE export:
# Drop both files at data/, then:
# data/cwec.csv — MITRE "view 1000" CSV
# data/cwec_v4.20.xml — full XML (needed for Categories, Views,
# Demonstrative_Examples, Mapping_Notes,
# References, Content_History, Audience)
crystal run data/build_data.cr
crystal spec
The build is incremental — without the XML the catalog still loads, it just won't include Categories, Views, or any of the XML-only blocks.
The embedded data lives at src/cwe/data/weaknesses.json and is read into the binary at compile time via {{ read_file(...) }}.
License
MIT © hahwul
This library redistributes data from the MITRE Common Weakness Enumeration, which is released under MITRE's terms of use. CWE™ is a trademark of The MITRE Corporation.
cwe.cr
- 1
- 0
- 0
- 0
- 0
- about 3 hours ago
- May 19, 2026
MIT License
Tue, 19 May 2026 15:25:21 GMT