| name | implementing-patch-management-for-ot-systems |
| description | Implements a structured patch management program for OT/ICS environments where IT-style patching can cause process disruption or safety hazards, covering vendor compatibility testing, risk-based prioritization, staged test deployment, maintenance window coordination, rollback procedures, and compensating controls. Use when planning or auditing patching for SCADA, PLCs, or other industrial control systems.
|
| domain | cybersecurity |
| subdomain | ot-ics-security |
| tags | ["ot-security","ics","scada","industrial-control","iec62443","patch-management","vulnerability-management"] |
| version | 1.0.0 |
| author | mahipal |
| license | Apache-2.0 |
| nist_csf | ["PR.IR-01","DE.CM-01","ID.AM-05","GV.OC-02"] |
| mitre_attack | ["T1078","T1190","T1059","T0816","T0836"] |
Implementing Patch Management for OT Systems
When to Use
- When establishing a formal OT patch management program for the first time
- When responding to critical ICS-CERT advisories affecting deployed OT systems
- When preparing for NERC CIP-007-6 or IEC 62443 patch management compliance audits
- When planning patch deployment during limited maintenance windows in continuous operations
- When evaluating compensating controls for systems that cannot be patched
Do not use for IT-only patch management without OT considerations, for emergency patching during active cyber incidents (see performing-ot-incident-response), or for firmware upgrades that change PLC functionality (requires separate change management).
Prerequisites
- OT asset inventory with firmware/OS versions for all patchable systems
- Vendor patch notification subscriptions (Siemens ProductCERT, Rockwell, Schneider, etc.)
- Test/staging environment mirroring production OT systems for patch validation
- Maintenance window schedule aligned with process shutdowns and turnarounds
- Change management board approval process including operations and safety representatives
Workflow
Step 1: Establish OT Patch Management Program
Define the patch management lifecycle adapted for OT environments where availability and safety take priority over immediate vulnerability remediation.
"""OT Patch Management Program Manager.
Tracks patches for OT systems, manages risk-based prioritization,
coordinates testing and deployment, and documents compensating
controls for unpatchable systems.
"""
import json
import sys
from collections import defaultdict
from dataclasses import dataclass, field, asdict
from datetime import datetime, timedelta
from enum import Enum
class PatchStatus(str, Enum):
IDENTIFIED = "identified"
EVALUATING = "evaluating"
TESTING = "testing"
APPROVED =
SCHEDULED =
DEPLOYED =
DEFERRED =
NOT_APPLICABLE =
:
patch_id:
vendor:
product:
affected_versions:
cve_ids:
cvss_score:
ics_cert_advisory:
description:
status: = PatchStatus.IDENTIFIED
identified_date: =
evaluation_deadline: =
test_date: =
deployment_date: =
affected_assets: = field(default_factory=)
test_results: =
compensating_controls: =
risk_rating: =
maintenance_window: =
rollback_procedure: =
:
():
.patches = []
.assets = {}
.vendor_feeds = {}
():
patch.evaluation_deadline:
identified = datetime.fromisoformat(patch.identified_date)
patch.evaluation_deadline = (identified + timedelta(days=)).isoformat()
.patches.append(patch)
():
patch .patches:
patch.status (PatchStatus.DEPLOYED, PatchStatus.NOT_APPLICABLE):
score = patch.cvss_score
patch.ics_cert_advisory:
score +=
asset_id patch.affected_assets:
asset = .assets.get(asset_id, {})
asset.get():
score +=
asset.get() (, ):
score +=
score = (score, )
score >= :
patch.risk_rating =
score >= :
patch.risk_rating =
score >= :
patch.risk_rating =
:
patch.risk_rating =
():
now = datetime.now()
approaching = []
patch .patches:
patch.status == PatchStatus.IDENTIFIED:
deadline = datetime.fromisoformat(patch.evaluation_deadline)
days_remaining = (deadline - now).days
days_remaining <= :
approaching.append((patch, days_remaining))
(approaching, key= x: x[])
():
patch .patches:
patch.patch_id == patch_id:
patch.status = PatchStatus.DEFERRED
patch.compensating_controls = compensating_controls
patch.test_results =
():
.prioritize_patches()
report = []
report.append( * )
report.append()
report.append()
report.append( * )
status_counts = defaultdict()
p .patches:
status_counts[p.status] +=
report.append()
status, count status_counts.items():
report.append()
approaching = .get_patches_needing_evaluation()
approaching:
report.append()
patch, days approaching:
report.append()
urgent = [p p .patches
p.risk_rating (, )
p.status (PatchStatus.DEPLOYED, PatchStatus.NOT_APPLICABLE)]
urgent:
report.append()
p urgent:
report.append()
report.append()
report.append()
report.append()
deferred = [p p .patches p.status == PatchStatus.DEFERRED]
deferred:
report.append()
p deferred:
report.append()
report.append()
report.append()
.join(report)
__name__ == :
manager = OTPatchManager()
manager.add_patch(OTPatch(
patch_id=,
vendor=,
product=,
affected_versions=,
cve_ids=[],
cvss_score=,
ics_cert_advisory=,
description=,
identified_date=,
affected_assets=[, , ],
))
manager.add_patch(OTPatch(
patch_id=,
vendor=,
product=,
affected_versions=,
cve_ids=[],
cvss_score=,
ics_cert_advisory=,
description=,
identified_date=,
affected_assets=[, ],
))
(manager.generate_report())