#!/usr/bin/env python3
"""Scope CVE-2026-55255 exposure from asset inventories and exported product logs.
Input: a directory containing CSV, JSON, JSONL, TXT, LOG, YAML, or XML exports.
The script does not contact the network. It looks for the CVE, vendor/product
selectors, and affected-version selectors recorded in the Halting Problems profile.
"""
from __future__ import annotations
import csv, json, os, sys
from pathlib import Path
CVE_ID = "CVE-2026-55255"
VENDOR = "Langflow"
PRODUCT = "Langflow"
AFFECTED_VERSION_SELECTORS = [
"langflow-langflow < 1.9.1"
]
SOURCE_SELECTORS = [
"https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
"https://nvd.nist.gov/vuln/detail/CVE-2026-55255",
"https://github.com/langflow-ai/langflow/commit/2c9f498d664a3c32698b57d7c5e752625291060e",
"https://github.com/langflow-ai/langflow/pull/12832",
"https://github.com/langflow-ai/langflow/security/advisories/GHSA-qrpv-q767-xqq2",
"https://webflow.sysdig.com/blog/understanding-langflow-cve-2026-55255-and-why-higher-cvss-vulnerabilities-arent-always-the-most-exploited",
"https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-55255"
]
DOMAINS = []
HASHES = []
URLS = []
FILES = []
PROCESS_PATTERNS = [PRODUCT, VENDOR, CVE_ID]
NETWORK_PATTERNS = []
INDICATORS = [
"CVE-2026-55255",
"Langflow",
"Langflow",
"langflow-langflow < 1.9.1",
"https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
"https://nvd.nist.gov/vuln/detail/CVE-2026-55255",
"https://github.com/langflow-ai/langflow/commit/2c9f498d664a3c32698b57d7c5e752625291060e",
"https://github.com/langflow-ai/langflow/pull/12832",
"https://github.com/langflow-ai/langflow/security/advisories/GHSA-qrpv-q767-xqq2",
"https://webflow.sysdig.com/blog/understanding-langflow-cve-2026-55255-and-why-higher-cvss-vulnerabilities-arent-always-the-most-exploited",
"https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-55255"
]
OUT = os.environ.get("OUT", "hp-langflow-cve-2026-55255-kev-scope")
TEXT_SUFFIXES = {'.csv','.json','.jsonl','.txt','.log','.yaml','.yml','.xml','.conf','.ini','.md'}
EXCLUDED_DIR_NAMES = {'.git','node_modules','vendor','dist','__pycache__','.venv'}
def read_text(path: Path) -> str:
try:
return path.read_text(encoding='utf-8', errors='ignore')
except Exception:
return ''
def iter_files(root: Path):
if root.is_file():
yield root; return
for p in root.rglob('*'):
if p.is_file() and not any(part in EXCLUDED_DIR_NAMES for part in p.parts):
if not p.suffix or p.suffix.lower() in TEXT_SUFFIXES:
yield p
def scan(root: Path):
matches=[]
lowered=[s.lower() for s in INDICATORS if s]
for path in iter_files(root):
text=read_text(path)
low=text.lower()
hits=sorted({INDICATORS[i] for i,s in enumerate(lowered) if s in low})
if hits:
matches.append({'path': str(path), 'hits': hits})
return matches
def main(argv=None):
argv=argv or sys.argv[1:]
root=Path(argv[0]) if argv else Path('.')
matches=scan(root)
out_dir=Path(OUT); out_dir.mkdir(parents=True, exist_ok=True)
report={'cve': CVE_ID, 'vendor': VENDOR, 'product': PRODUCT, 'affected_version_selectors': AFFECTED_VERSION_SELECTORS, 'match_count': len(matches), 'matches': matches}
(out_dir/'scope_report.json').write_text(json.dumps(report, indent=2), encoding='utf-8')
print(json.dumps({'cve': CVE_ID, 'match_count': len(matches), 'report': str(out_dir/'scope_report.json')}, indent=2))
return 2 if matches else 0
if __name__ == '__main__':
raise SystemExit(main())