Document Purpose: Technical documentation for Python-native CPE-AS generation system integrated with NVD-ish records
Created: January 12, 2026
Last Updated: January 25, 2026
Implementation: src/analysis_tool/core/cpe_as_generator.py
Tests: test_suites/nvd-ish_collector/test_cpe_as_integration.py
The CPE-AS generation system transforms CVE 5.0 affected array entries into NVD-style CPE Applicability Statement JSON using pure Python. This enables automated generation of machine-readable vulnerability configuration data from CVE records.
affected[] array entries with CPE base string(s)configurations[] arrays with CPE match objectsaffected[] arrayplatform_entry_registry.py CPE cachevendor - Vendor nameproduct - Product namedefaultStatus - affected, unaffected, or unknownversions[] - Array of version constraint objects (may be empty)platforms[] - Platform identifierspackageName - Package manager namecpes[] - Explicit CPE stringsrepo - Repository URLprogramFiles[] - Affected file pathsversions[] items may contain:
version - Exact version or wildcard (*). May be omitted for open-ended rangesstatus - affected, unaffected, unknown. Defaults to defaultStatus when omittedversionType - semver, custom, git, etc.lessThan - Upper bound (exclusive)lessThanOrEqual - Upper bound (inclusive)changes[] - Status changes at specific versionsImplementation Standard: All cpeMatch objects serialize properties in the following explicit order:
versionsEntryIndex - Maps to source versions[] array index (0-based, null if no versions array)appliedPattern - (Optional) Pattern reference for traceability (e.g., “exact.single”, “range.lessThan”, “inference.affectedFromWildcardExpansion”). Omitted (null) for truly unknown patterns.vulnerable - Boolean indicating vulnerability status (true for affected entries, false for metadata-only entries)criteria - (Optional) Full CPE 2.3 formatted string. Omitted for metadata-only objects.versionStartIncluding - (Optional) Lower bound version, inclusiveversionStartExcluding - (Optional) Lower bound version, exclusiveversionEndIncluding - (Optional) Upper bound version, inclusiveversionEndExcluding - (Optional) Upper bound version, exclusiveconcerns - (Optional) Array of concern identifiers for metadata-only or flagged entries (e.g., ["statusUnaffected"], ["updatePatternsInRange"])updatePattern) follow core propertiesRationale:
versionsEntryIndex, appliedPattern) appear first for easy debuggingvulnerable, criteria) follow immediatelyExample Property Order (Standard cpeMatch):
{
"versionsEntryIndex": 0,
"appliedPattern": "range.lessThan",
"vulnerable": true,
"criteria": "cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*",
"versionStartIncluding": "2.0",
"versionEndExcluding": "2.5"
}
Example Property Order (Metadata-only cpeMatch):
{
"versionsEntryIndex": 1,
"vulnerable": false,
"concerns": ["statusUnaffected"]
}
All examples throughout this document follow this ordering convention.
Single cpeMatch with wildcard version (*) when no specific version data is available or all versions are affected.
{
"versionsEntryIndex": 0, // Index of versions[] entry, or null if no versions array
"appliedPattern": "noVersion.allAffected",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:cpebasestring:*:*:*:*:*:*:*:*"
}
Note: versionsEntryIndex will be:
null when Pattern A (no versions[] array) is matched0 when Pattern A-Variant (explicit “*”), Pattern C (placeholder version), or Pattern D (defaultStatus=”unknown”) is matchedPattern A: Explicit “All Versions” via defaultStatus
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected"
// No versions[] array present
}
Pattern A-Variant: Explicit wildcard version
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected",
"versions": [
{"version": "*", "status": "affected"}
]
}
Pattern B: Empty versions array
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected",
"versions": []
}
Pattern C: Only Version Placeholder Values
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected",
"versions": [
{"version": "unspecified", "status": "affected"},
// OR
{"version": "all", "status": "affected"},
// OR
{"version": "n/a", "status": "affected"},
// OR other placeholder terminology
]
}
Pattern C-Variant: Placeholder version AND placeholder changes array
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected",
"versions": [
{
"version": "unspecified",
"status": "affected",
"changes": [
{"at": "unknown", "status": "unaffected"}
]
}
]
}
Result: Still Pattern 3.1 - all version-related data is placeholders
Pattern D: defaultStatus=”unknown” with no/empty versions
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "unknown"
// No versions[] array OR versions: []
}
Result: Metadata-only cpeMatch object
Output:
{
"versionsEntryIndex": null,
"vulnerable": false,
"concerns": ["defaultStatusUnknown"]
}
Logic:
concerns: ["defaultStatusUnknown"]VERSION_PLACEHOLDER_VALUES (Centralized in platform_entry_registry.py):
VERSION_PLACEHOLDER_VALUES = [
'unspecified', 'unknown', 'none', 'undefined', 'various',
'n/a', 'not available', 'not applicable', 'unavailable',
'na', 'nil', 'tbd', 'to be determined', 'pending',
'not specified', 'not determined', 'not known', 'not listed',
'not provided', 'missing', 'empty', 'null', '-',
'multiple versions', 'see references', 'see advisory',
'check', 'noted', 'all'
]
# Total: 28 patterns (case-insensitive)
# JavaScript adds: "*" (wildcard) to this list at runtime
# Note: 'all' included - observed in real CVE data as wildcard indicator
# Note: "0" is NOT a placeholder - treated as literal version value
Analysis:
When no platforms are vulnerable/affected, generate a metadata-only cpeMatch object to indicate the condition was processed.
[
{
"versionsEntryIndex": 0,
"vulnerable": false,
"concerns": ["noAffectedPlatforms"]
}
]
Pattern A: defaultStatus=affected with only unaffected versions
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected",
"versions": [
{"version": "1.0", "status": "unaffected"},
{"version": "2.0", "status": "unaffected"}
]
}
Pattern B: defaultStatus=unaffected (nothing to output)
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "unaffected"
// No versions[] or all versions are unaffected
}
Pattern C: Placeholder with unaffected status
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "affected",
"versions": [
{"version": "unspecified", "status": "unaffected"}
]
}
Pattern D: defaultStatus=’unknown’ with all unaffected
{
"vendor": "acme",
"product": "widget",
"defaultStatus": "unknown",
"versions": [
{"version": "1.0", "status": "unaffected"},
{"version": "2.0", "status": "unaffected"}
]
}
Pattern E: Changes array results in immediate unaffected
{
"version": "1.0",
"status": "affected",
"changes": [
{"at": "1.0", "status": "unaffected"}
]
}
Note: Change occurs at same version as base → no affected range exists
Logic:
vulnerable: false and concerns: ["noAffectedPlatforms"]Analysis:
One or more cpeMatch objects with exact version values in criteria field (no version range fields).
Pattern A: Single exact version
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.2.3”, “status”: “affected” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.2.3:::::::*“ }</pre> |
Pattern B: Multiple discrete versions (1:1 transformation)
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.2.3”, “status”: “affected” }, { “version”: “1.2.5”, “status”: “affected” }, { “version”: “2.0.1”, “status”: “affected” } ] }</pre> |
<pre>[ { “versionsEntryIndex”: 0, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.2.3:::::::“ }, { “versionsEntryIndex”: 1, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.2.5:::::::“ }, { “versionsEntryIndex”: 2, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:2.0.1:::::::*“ } ]</pre> |
Note: Three version entries produce three cpeMatch objects (1:1 transformation per entry)
Pattern C: Mixed affected/unaffected/unknown versions
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.0”, “status”: “affected” }, { “version”: “1.1”, “status”: “unaffected” }, { “version”: “1.2”, “status”: “affected” }, { “version”: “2.0”, “status”: “unknown” } ] }</pre> |
<pre>[ { “versionsEntryIndex”: 0, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.0:::::::“ }, { “versionsEntryIndex”: 1, “vulnerable”: false, “concerns”: [“statusUnaffected”] }, { “versionsEntryIndex”: 2, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.2:::::::“ }, { “versionsEntryIndex”: 3, “vulnerable”: false, “concerns”: [“statusUnknown”] } ]</pre> |
Note: All four version entries produce cpeMatch objects. Affected versions generate full criteria; unaffected and unknown versions produce metadata-only objects with concerns array.
Pattern D: defaultStatus conflict - version.status takes precedence
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “defaultStatus”: “unaffected”, “versions”: [ { “version”: “1.2.3”, “status”: “affected” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.2.3:::::::*“ }</pre> |
Note: version.status=’affected’ overrides defaultStatus=’unaffected’ - produces vulnerable=true output
Logic (per version entry):
concerns: ["statusUnaffected"]concerns: ["statusUnknown"]Analysis:
Iteration Example (1:1 transformation):
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “versions”: [ {“version”: “1.0”, “status”: “affected”}, {“version”: “2.0”, “status”: “affected”}, {“version”: “3.0”, “status”: “affected”} ] }</pre> |
<pre>[ { “versionsEntryIndex”: 0, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:1.0:::::::“ }, { “versionsEntryIndex”: 1, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:2.0:::::::“ }, { “versionsEntryIndex”: 2, “appliedPattern”: “exact.single”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product:3.0:::::::*“ } ]</pre> |
Note: Three version entries produce three cpeMatch objects (1:1 transformation per entry)
Single cpeMatch object with version range properties (versionStartIncluding/Excluding, versionEndIncluding/Excluding).
Pattern A: Explicit range with lessThan
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “0”, “status”: “affected”, “lessThan”: “2.0” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.lessThan”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “0”, “versionEndExcluding”: “2.0” }</pre> |
Pattern B: Explicit range with lessThanOrEqual
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.0”, “status”: “affected”, “lessThanOrEqual”: “1.9.5” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.lessThanOrEqual”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “1.0”, “versionEndIncluding”: “1.9.5” }</pre> |
Note on Update Patterns in Ranges: If range boundaries contain update patterns (e.g., "10.0 SP 1" to "10.0 SP 3"), current implementation DETECTS but does NOT APPLY transformations. The original untransformed values are used in versionStartIncluding/versionEndExcluding. This is flagged in metadata with blocked_by_ranges: true for visibility.
Pattern C: Wildcard in lessThanOrEqual (open-ended)
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.0”, “status”: “affected”, “lessThanOrEqual”: “*“ } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.openEnd”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “1.0” }</pre> |
Note: Wildcard upper bound means “all versions from 1.0 onward” - versionEnd field omitted
Pattern D: Single status change (changes array)
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “5.0”, “status”: “affected”, “changes”: [ { “at”: “5.0.3”, “status”: “unaffected” } ] } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.changesFixed”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “5.0”, “versionEndExcluding”: “5.0.3” }</pre> |
Note: Range from base version to change point
Pattern D-Variant: Placeholder version with real changes data
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “unspecified”, “status”: “affected”, “changes”: [ { “at”: “1.2.3”, “status”: “unaffected” } ] } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.placeholderChanges”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionEndExcluding”: “1.2.3” }</pre> |
Note: version field is placeholder - versionStart omitted, changes[].at provides end boundary
Pattern E: Inverse single change (unaffected→affected)
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.0”, “status”: “unaffected”, “changes”: [ { “at”: “1.5”, “status”: “affected” } ] } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.changesIntroduced”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “1.5” }</pre> |
Note: Range starts where status changes to affected, no end boundary (open-ended)
Pattern F: Open-ended beginning (no version field)
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “status”: “affected”, “lessThan”: “2.0” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.openStart”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionEndExcluding”: “2.0” }</pre> |
Note: Everything before 2.0 is affected - versionStart omitted
Pattern G: Placeholder in upper bound
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.0”, “status”: “affected”, “lessThan”: “unknown” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.placeholderUpperBound”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “1.0” }</pre> |
Note: Placeholder in lessThan treated as open-ended - versionEnd omitted
Pattern H: version=”0” special case
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “0”, “status”: “affected”, “lessThan”: “2.0” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.zeroStart”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “0”, “versionEndExcluding”: “2.0” }</pre> |
Note: “0” is treated as a literal version value, not a placeholder. Current implementation includes “0” NOT in VERSION_PLACEHOLDER_VALUES, so it outputs versionStartIncluding="0" explicitly. Infer Affected Ranges also defaults to “0” when no start version is available.
Pattern I: defaultStatus override - version.status takes precedence
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “defaultStatus”: “unaffected”, “versions”: [ { “version”: “2.0”, “status”: “affected”, “lessThan”: “3.0” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “range.statusOverride”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “2.0”, “versionEndExcluding”: “3.0” }</pre> |
Note: version.status=’affected’ overrides defaultStatus=’unaffected’ - produces vulnerable=true output
Logic (per version entry):
concerns: ["statusUnaffected"]concerns: ["statusUnknown"]Analysis:
version field typically = starting point (inclusive by default)Iteration Example (1:1 transformation):
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “versions”: [ { “version”: “1.0”, “status”: “affected”, “lessThan”: “2.0” }, { “version”: “3.0”, “status”: “affected”, “lessThan”: “4.0” } ] }</pre> |
<pre>[ { “versionsEntryIndex”: 0, “appliedPattern”: “range.lessThan”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “1.0”, “versionEndExcluding”: “2.0” }, { “versionsEntryIndex”: 1, “appliedPattern”: “range.lessThan”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “3.0”, “versionEndExcluding”: “4.0” } ]</pre> |
Note: Two range entries produce two cpeMatch objects (1:1 transformation per entry)
Single version entry with multiple status transitions producing multiple ranges.
Implementation Status: Not currently implemented in JavaScript - documented for Python implementation requirements.
Pattern A: Multiple status flip-flops
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “versions”: [ { “version”: “3.0”, “status”: “affected”, “changes”: [ { “at”: “3.0.5”, “status”: “unaffected” }, { “at”: “3.1.0”, “status”: “affected” }, { “at”: “3.1.2”, “status”: “unaffected” } ] } ] }</pre> |
<pre>[ { “versionsEntryIndex”: 0, “appliedPattern”: “multiRange.exactStatusTransitions”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “3.0”, “versionEndExcluding”: “3.0.5” }, { “versionsEntryIndex”: 0, “appliedPattern”: “multiRange.exactStatusTransitions”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “3.1.0”, “versionEndExcluding”: “3.1.2” } ]</pre> |
Note: affected → unaffected → affected → unaffected creates two vulnerable ranges (vulnerability reintroduced)
Pattern B: Changes combined with range bounds
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “versions”: [ { “version”: “2.0”, “status”: “affected”, “lessThan”: “5.0”, “changes”: [ { “at”: “3.0”, “status”: “unaffected” }, { “at”: “4.0”, “status”: “affected” } ] } ] }</pre> |
<pre>[ { “versionsEntryIndex”: 0, “appliedPattern”: “multiRange.rangeStatusTransitions”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “2.0”, “versionEndExcluding”: “3.0” }, { “versionsEntryIndex”: 0, “appliedPattern”: “multiRange.rangeStatusTransitions”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “4.0”, “versionEndExcluding”: “5.0” } ]</pre> |
Note: Changes array splits the overall lessThan range into sub-ranges based on status transitions
Logic:
Analysis:
Patterns requiring version ordering knowledge, inference, or complex interpretation.
Implementation Priority: DEFERRED - Advanced feature with parsing complexity
Wildcard version patterns translated to version ranges.
Pattern A: Wildcard in lessThanOrEqual
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “5.4.0”, “status”: “affected”, “lessThanOrEqual”: “5.4.*“ } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “inference.affectedFromWildcardExpansion”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “5.4.0”, “versionEndExcluding”: “5.5.0” }</pre> |
Note: Wildcard “5.4.*” expanded to range [5.4.0, 5.5.0) by incrementing minor version
Pattern B: Wildcard in version field
| Input Pattern | Output cpeMatch Object |
|---|---|
| <pre>{ “versions”: [ { “version”: “2.*”, “status”: “affected” } ] }</pre> |
<pre>{ “versionsEntryIndex”: 0, “appliedPattern”: “inference.affectedFromWildcardExpansion”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “2.0”, “versionEndExcluding”: “3.0” }</pre> |
Note: Wildcard “2.*” expanded to range [2.0, 3.0) by incrementing major version
Logic:
Analysis:
Implementation Priority: DEFERRED - High-risk inference pattern with soundness concerns
Inferred vulnerable ranges from defaultStatus=’affected’ with unaffected RANGES (not exact versions).
Applicability Constraint: Infer Affected Ranges ONLY applies when unaffected versions have range constraints (lessThan, lessThanOrEqual). Exact unaffected versions without range constraints do NOT trigger inferred affected range detection.
Pattern A: Gaps between unaffected ranges
| Input Pattern | Output cpeMatch Objects |
|---|---|
| <pre>{ “defaultStatus”: “affected”, “versions”: [ { “version”: “1.0”, “status”: “unaffected”, “lessThan”: “2.0” }, { “version”: “3.0”, “status”: “unaffected”, “lessThan”: “4.0” }, { “version”: “5.0”, “status”: “unaffected”, “lessThanOrEqual”: “6.0” } ] }</pre> |
<pre>[ { “versionsEntryIndex”: null, “appliedPattern”: “inference.affectedFromUnaffectedRanges”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionEndExcluding”: “1.0” }, { “versionsEntryIndex”: null, “appliedPattern”: “inference.affectedFromUnaffectedRanges”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “2.0”, “versionEndExcluding”: “3.0” }, { “versionsEntryIndex”: null, “appliedPattern”: “inference.affectedFromUnaffectedRanges”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartIncluding”: “4.0”, “versionEndExcluding”: “5.0” }, { “versionsEntryIndex”: null, “appliedPattern”: “inference.affectedFromUnaffectedRanges”, “vulnerable”: true, “criteria”: “cpe:2.3:a:vendor:product::::::::”, “versionStartExcluding”: “6.0” } ]</pre> |
Note: Unaffected ranges: [1.0-2.0), [3.0-4.0), [5.0-6.0]. Gaps filled with affected ranges.
Logic:
Transformations applied before pattern matching (preprocessing) and after output generation (postprocessing).
Implementation Status: Fully implemented in JavaScript - comprehensive pattern library (500+ lines)
Version strings containing update/patch/service pack indicators must be parsed into base version + update component.
Supported Term Groups (standardized output):
sp (Service Pack, SP)patch (Patch, p)hotfix (Hotfix, HF)update (Update)mr (Maintenance Release, MR)build (Build)release (Release)milestone (Milestone)snapshot (Snapshot)preview (Preview)candidate (Candidate)development (Development)dp (Device Pack, DP)Pattern Recognition (each term group supports):
"10.0 SP 1" → 10.0:sp1"7.0.1update2" → 7.0.1:update2"1.2.3-patch.4" → 1.2.3:patch4"16.0.0_mr_7" → 16.0.0:mr7Output: Base version in criteria position 5, update component in position 6 (normalized: lowercase, no spaces)
Application Scope:
Current JavaScript Behavior: Update patterns in range boundaries are detected and logged with blocked_by_ranges: true flag, but the original version string is used unchanged for versionStartIncluding/versionEndExcluding properties.
Implementation Note: JavaScript uses regex pattern library with specific-to-general ordering. Python implementation should port this proven logic.
Implementation Status: Fully implemented in JavaScript
When multiple cpeMatch objects share the same base CPE components but differ in the update field, enforce non-overlapping specificity.
Problem: Version extraction may generate both:
cpe:2.3:a:vendor:product:1.0:*:... (any update)cpe:2.3:a:vendor:product:1.0:patch1:... (specific update)Resolution: Convert wildcard update (*) to no-update (-) when specific updates exist for same base, ensuring each match has distinct scope
Example:
| Before | After |
|---|---|
cpe:...:1.0:*:...cpe:...:1.0:patch1:... |
cpe:...:1.0:-:...cpe:...:1.0:patch1:... |
Logic:
*) and specific update values- (no update) to eliminate overlapApplication: Applied after all pattern processing complete, before final output
Generated CPE-AS data is embedded in the NVD-ish record’s enrichedCVEv5Affected.cveListV5AffectedEntries[].cpeAsGeneration section.
Each CVE 5.0 affected[] entry produces one analysis entry with generated cpeMatch objects.
Output: NVD-ish record with generated CPE-AS (partial - showing only enrichedCVEv5Affected structure)
{
"id": "CVE-2024-1234",
"sourceIdentifier": "cna@example.com",
"published": "2024-01-15T00:00:00.000",
"lastModified": "2024-01-15T00:00:00.000",
"vulnStatus": "Analyzed",
"descriptions": [...],
"references": [...],
"metrics": {...},
"enrichedCVEv5Affected": {
"toolExecutionMetadata": {...},
"cpeDeterminationMetadata": [...],
"cveListV5AffectedEntries": [
{
"originAffectedEntry": {
"sourceId": "cna@example.com",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[0]",
"vendor": "example",
"product": "webapp",
"defaultStatus": "unaffected",
"versions": [
{
"version": "1.0",
"status": "affected"
},
{
"version": "2.0",
"status": "affected",
"lessThan": "2.5"
},
{
"version": "3.0",
"status": "affected",
"changes": [
{
"at": "3.2.1",
"status": "unaffected"
}
]
}
]
},
"sourceDataConcerns": {...},
"aliasExtraction": {...},
"cpeDetermination": {...},
"cpeAsGeneration": {
"sourceId": "Hashmire/Analysis_Tools v0.3.0",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[0]",
"generatedCpeMatch": [
{
"versionsEntryIndex": 0,
"appliedPattern": "exact.single",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:webapp:1.0:*:*:*:*:*:*:*"
},
{
"versionsEntryIndex": 1,
"appliedPattern": "range.lessThan",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:webapp:*:*:*:*:*:*:*:*",
"versionStartIncluding": "2.0",
"versionEndExcluding": "2.5"
},
{
"versionsEntryIndex": 2,
"appliedPattern": "range.changesFixed",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:webapp:*:*:*:*:*:*:*:*",
"versionStartIncluding": "3.0",
"versionEndExcluding": "3.2.1"
}
]
}
},
{
"originAffectedEntry": {
"sourceId": "cna@example.com",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[1]",
"vendor": "example",
"product": "library",
"defaultStatus": "affected",
"versions": []
},
"sourceDataConcerns": {...},
"aliasExtraction": {...},
"cpeDetermination": {...},
"cpeAsGeneration": {
"sourceId": "Hashmire/Analysis_Tools v0.3.0",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[1]",
"generatedCpeMatch": [
{
"versionsEntryIndex": null,
"appliedPattern": "noVersion.allAffected",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:library:*:*:*:*:*:*:*:*"
}
]
}
},
{
"originAffectedEntry": {
"sourceId": "cna@example.com",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[2]",
"vendor": "example",
"product": "server",
"defaultStatus": "unaffected",
"versions": [
{
"version": "16.0.0 MR 7",
"status": "affected"
}
]
},
"sourceDataConcerns": {...},
"aliasExtraction": {...},
"cpeDetermination": {...},
"cpeAsGeneration": {
"sourceId": "Hashmire/Analysis_Tools v0.3.0",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[2]",
"generatedCpeMatch": [
{
"versionsEntryIndex": 0,
"appliedPattern": "exact.single",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:server:16.0.0:mr7:*:*:*:*:*:*"
}
]
}
},
{
"originAffectedEntry": {
"sourceId": "cna@example.com",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[3]",
"vendor": "example",
"product": "platform",
"defaultStatus": "affected",
"versions": [
{
"version": "abc123def",
"versionType": "git",
"status": "affected"
},
{
"version": "10.0 SP 1",
"status": "affected",
"lessThanOrEqual": "10.0 SP 3"
}
]
},
"sourceDataConcerns": {...},
"aliasExtraction": {...},
"cpeDetermination": {...},
"cpeAsGeneration": {
"sourceId": "Hashmire/Analysis_Tools v0.3.0",
"cvelistv5AffectedEntryIndex": "cve.containers.cna.affected.[3]",
"generatedCpeMatch": [
{
"versionsEntryIndex": 0,
"concerns": ["versionTypeGit"]
},
{
"versionsEntryIndex": 1,
"appliedPattern": "range.lessThanOrEqual",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:platform:*:*:*:*:*:*:*:*",
"versionStartIncluding": "10.0 SP 1",
"versionEndIncluding": "10.0 SP 3",
"concerns": ["updatePatternsInRange"]
}
]
}
}
]
}
}
| affected[] Entry | Applied Pattern | cpeMatch Objects in cpeAsGeneration |
|---|---|---|
| webapp v1.0 (exact) | Section 3.3 Pattern A | Single exact version: criteria="...webapp:1.0:...", appliedPattern="exact.single" |
| webapp v2.0-2.5 (range) | Section 3.4 Pattern A | Single range: versionStartIncluding="2.0", versionEndExcluding="2.5", appliedPattern="range.lessThan" |
| webapp v3.0 with change at 3.2.1 | Section 3.4 Pattern D | Single range: versionStartIncluding="3.0", versionEndExcluding="3.2.1", appliedPattern="range.changesFixed" |
| library (no versions) | Section 3.1 Pattern A | Single wildcard: criteria="...library:*:...", appliedPattern="noVersion.allAffected" |
| server v16.0.0 MR 7 (update pattern) | Section 3.3 Pattern A + Section 4.1 | Exact version with update component: criteria="...server:16.0.0:mr7:...", appliedPattern="exact.single" |
| platform git commit (versionType: git) | Section 6.1 | Metadata-only: versionsEntryIndex=0, concerns=["versionTypeGit"] |
| platform 10.0 SP 1-3 (update in range) | Section 3.4 Pattern B + Section 6.2 | Range with untransformed update patterns: versionStartIncluding="10.0 SP 1", concerns=["updatePatternsInRange"] |
affected[] produces one complete analysis entryThis section catalogs known failure conditions, edge cases, and limitations in CVE 5.x to CPE-AS translation logic. These represent areas where automated translation is not possible, produces incomplete results, or requires human review.
Documentation Requirements: All limitations MUST be documented in affected cpeMatch objects via the concerns[] array and logged to audit trail with specific CVE ID, affected entry index, and limitation details.
The concerns array in each cpeMatch object identifies conditions that may require human review or special handling. These concern identifiers enable programmatic filtering and analysis of potential data quality issues.
| Concern Identifier | Trigger Condition | cpeMatch Type | Notes |
|---|---|---|---|
| Status-Based Concerns | |||
statusUnaffected |
affected.[*].versions[*].status: "unaffected" |
Metadata-only | Version explicitly marked as unaffected |
statusUnknown |
affected.[*].versions[*].status: "unknown" |
Metadata-only | Version status is unknown/unclear |
defaultStatusUnknown |
affected.[*].defaultStatus: "unknown" |
Metadata-only | Default status for entry is unknown |
noAffectedPlatforms |
affected.[*] has no platforms or all unaffected |
Metadata-only | Entry has no platform data |
| Version Type Concerns | |||
versionTypeGit |
affected.[*].versions[*].versionType: "git" |
Metadata-only | Git commit hashes (not semantic versions) |
| CPE Mapping Concerns | |||
cpeUnconfirmedWithSuggestions |
No confirmed CPE mapping, but CPE suggestions exist | Metadata-only | Requires manual CPE confirmation |
cpeUnconfirmedNoSuggestions |
No confirmed CPE mapping or suggestions | Metadata-only | Requires manual CPE research |
| Pattern Detection Concerns | |||
inferredAffectedFromWildcardExpansion |
affected.[*].versions[*].version contains "*" pattern |
Metadata-only or Full | appliedPattern = "inference.affectedFromWildcardExpansion" |
updatePatternsInRange |
affected.[*].versions[*].lessThan* contains update patterns |
Full cpeMatch | Version boundaries contain update patterns (untransformed) |
patternUnsupported |
No recognized pattern matched version entry structure | Metadata-only | appliedPattern omitted (null) |
Implementation Guideline: All conditions are logged with specific CVE ID, affected entry index, and reason. Multiple concerns may be present in a single cpeMatch object’s concerns[] array.
Pattern Tracking: The appliedPattern field distinguishes between known patterns with values (like "inference.affectedFromWildcardExpansion") and truly unknown patterns where the field is omitted (null) with patternUnsupported concern.
Issue: Certain version ordering schemes cannot be processed with general alphanumeric comparison.
General Approach: Every version entry is processed and generates a cpeMatch object. Incompatible version types generate metadata-only cpeMatch objects with concerns rather than being skipped entirely.
Processing Rules:
versionType: "git": CANNOT generate valid CPE criteria (commit hashes require graph ordering)
criteria, no vulnerable, no version range fields)concerns: ["versionTypeGit"] to indicate processing limitationFailure Condition: "versionType": "git"
Required Behavior:
versionsEntryIndex metadata fieldconcerns: ["versionTypeGit"]appliedPattern (no pattern was applied)criteria, vulnerable, or version range fieldsAudit Log Format:
"Detected 'versionType': 'git' | Cannot generate CPE criteria | Generated metadata-only cpeMatch | cve.containers.cna.affected.[X].versions[Y]"
Example Scenarios:
Scenario 1: Mixed versionTypes within single affected entry
Input:
{
"vendor": "example",
"product": "webapp",
"versions": [
{"version": "1.0.0", "status": "affected"},
{"version": "abc123def", "versionType": "git", "status": "affected"},
{"version": "2.0.0", "status": "affected"}
]
}
Output: 3 cpeMatch objects generated
[
{
"versionsEntryIndex": 0,
"appliedPattern": "exact.single",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:webapp:1.0.0:*:*:*:*:*:*:*"
},
{
"versionsEntryIndex": 1,
"concerns": ["versionTypeGit"]
},
{
"versionsEntryIndex": 2,
"appliedPattern": "exact.single",
"vulnerable": true,
"criteria": "cpe:2.3:a:example:webapp:2.0.0:*:*:*:*:*:*:*"
}
]
Scenario 2: Only git versionType
Input:
{
"vendor": "example",
"product": "library",
"versions": [
{"version": "abc123def", "versionType": "git", "status": "affected"}
]
}
Output: 1 metadata-only cpeMatch object
[
{
"versionsEntryIndex": 0,
"concerns": ["versionTypeGit"]
}
]
Issue: CPE 2.3 specification lacks mechanism to express update/patch components in range boundary values.
Failure Condition: Range boundaries (lessThan, lessThanOrEqual) contain update pattern indicators (e.g., "10.0 SP 1", "7.0 Update 3")
Required Behavior:
versionStartIncluding/versionEndExcluding properties"updatePatternsInRange" in concerns[] arrayExample:
Input:
{
"version": "10.0 SP 1",
"status": "affected",
"lessThanOrEqual": "10.0 SP 3"
}
Required Output:
{
"versionsEntryIndex": 0,
"appliedPattern": "range.lessThanOrEqual",
"vulnerable": true,
"criteria": "cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*",
"versionStartIncluding": "10.0 SP 1",
"versionEndIncluding": "10.0 SP 3",
"concerns": ["updatePatternsInRange"]
}
Audit Log Format:
"Detected update pattern in range boundary for cve.containers.cna.affected.[0].versions[1] | CPE-AS will use untransformed values for boundaries |
Rationale:
Impact:
"10.0 SP 1" vs "10.0 SP 3" is unreliableconcerns array enables automated filtering and manual review workflowsIssue: Wildcard patterns in range boundaries (e.g., "lessThanOrEqual": "2.4.*") require expansion logic that may not align with vendor versioning schemes.
Failure Condition: Range boundary contains wildcard character (*) in version string
Current Behavior:
inferredAffectedFromWildcardExpansion in concerns array if identified.Issue: Version strings with non-standard formatting may not parse correctly or compare as expected.
Examples:
"2024-01-15", "20240115""1.2.3.4.5.6""Windows 11" vs "10.0.22000""1:2.3.4-5"Current Behavior: General alphanumeric comparison without semantic awareness
Limitations:
Impact: Incorrect version ordering, potential gaps or overlaps in ranges
Mitigation: Leverage versionType field when available, manual review for non-standard schemes