fix(robustness): null guard WMI reads, dispose SHA256, abort LSV2 timeout (ISS-001, ISS-005, ISS-006)

ISS-001: MachineFingerprint.GetFingerprint() — null-safe WMI property reads
  - processorID and SerialNumber accessed with ?. and ?? string.Empty
  - prevents NullReferenceException on machines with missing WMI properties

ISS-005: LicenseValidator.Validate() — SHA256CryptoServiceProvider in using block
  - disposed on all code paths (success and failure)

ISS-006: Lsv2Client.Connect() — close TcpClient on timeout
  - _tcp.Close() called when ConnectAsync.Wait() times out
  - aborts pending socket task instead of leaking thread-pool slot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dtrentin 2026-05-18 23:00:08 +02:00
parent d95ead59af
commit 4cd7715594
5 changed files with 20 additions and 12 deletions

View file

@ -165,7 +165,7 @@ Completed: 2026-05-18
| Phase | Name | Plans | Status | Completed | | Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------| |-------|------|-------|--------|-----------|
| 13 | robustness-fixes | TBD | Not started | - | | 13 | robustness-fixes | 1 | Planning | - |
| 14 | test-hardening | TBD | Not started | - | | 14 | test-hardening | TBD | Not started | - |
### Phase 13: robustness-fixes ### Phase 13: robustness-fixes

View file

@ -10,22 +10,23 @@ See: .paul/PROJECT.md (updated 2026-05-18)
## Current Position ## Current Position
Milestone: v0.4 Robustness — **In Progress** Milestone: v0.4 Robustness — **In Progress**
Phase: 13 of 14 (robustness-fixes) — Not started Phase: 13 of 14 (robustness-fixes) — Planning
Plan: Not started Plan: 13-01 created, awaiting approval
Status: Ready to plan Status: PLAN created, ready for APPLY
Last activity: 2026-05-18 — Milestone v0.4 created Last activity: 2026-05-18 — Created .paul/phases/13-robustness-fixes/13-01-PLAN.md
Progress: Progress:
- Milestone v0.1: [██████████] 100% (complete) - Milestone v0.1: [██████████] 100% (complete)
- Milestone v0.2: [██████████] 100% (complete) - Milestone v0.2: [██████████] 100% (complete)
- Milestone v0.3: [██████████] 100% (complete) - Milestone v0.3: [██████████] 100% (complete)
- Milestone v0.4: [░░░░░░░░░░] 0% - Milestone v0.4: [░░░░░░░░░░] 0%
- Phase 13: [░░░░░░░░░░] 0%
## Loop Position ## Loop Position
``` ```
PLAN ──▶ APPLY ──▶ UNIFY PLAN ──▶ APPLY ──▶ UNIFY
○ ○ ○ [Ready for Phase 13 PLAN] ✓ ✓ ○ [Plan 13-01 applied, ready for UNIFY]
``` ```
## What Was Built (v0.3 Code Quality — complete) ## What Was Built (v0.3 Code Quality — complete)
@ -163,8 +164,8 @@ Documents: `.paul/codebase/` (7 files — STACK, ARCHITECTURE, STRUCTURE, CONVEN
Last session: 2026-05-18 Last session: 2026-05-18
Stopped at: v0.4 Robustness milestone created, ready to plan Phase 13 Stopped at: v0.4 Robustness milestone created, ready to plan Phase 13
Next action: /paul:plan — Phase 13 (robustness-fixes): ISS-001, ISS-005, ISS-006 Next action: Review and approve plan, then run /paul:apply .paul/phases/13-robustness-fixes/13-01-PLAN.md
Resume file: .paul/ROADMAP.md Resume file: .paul/phases/13-robustness-fixes/13-01-PLAN.md
--- ---
*STATE.md — Updated after every significant action* *STATE.md — Updated after every significant action*

View file

@ -30,8 +30,12 @@ namespace NcProgramManager.Cnc.Heidenhain
_tcp = new TcpClient(); _tcp = new TcpClient();
_tcp.SendTimeout = (int)_config.ConnectTimeout.TotalMilliseconds; _tcp.SendTimeout = (int)_config.ConnectTimeout.TotalMilliseconds;
_tcp.ReceiveTimeout = (int)_config.ConnectTimeout.TotalMilliseconds; _tcp.ReceiveTimeout = (int)_config.ConnectTimeout.TotalMilliseconds;
if (!_tcp.ConnectAsync(_config.IpAddress, _config.Port).Wait(_config.ConnectTimeout)) var connectTask = _tcp.ConnectAsync(_config.IpAddress, _config.Port);
if (!connectTask.Wait((int)_config.ConnectTimeout.TotalMilliseconds))
{
try { _tcp.Close(); } catch { }
return false; return false;
}
_stream = _tcp.GetStream(); _stream = _tcp.GetStream();
return Login(); return Login();
} }

View file

@ -29,7 +29,10 @@ namespace NcProgramManager.Licensing
using (var rsa = new RSACryptoServiceProvider()) using (var rsa = new RSACryptoServiceProvider())
{ {
rsa.FromXmlString(_publicKeyXml); rsa.FromXmlString(_publicKeyXml);
return rsa.VerifyData(data, new SHA256CryptoServiceProvider(), signature); using (var sha256 = new SHA256CryptoServiceProvider())
{
return rsa.VerifyData(data, sha256, signature);
}
} }
} }
catch catch

View file

@ -13,7 +13,7 @@ namespace NcProgramManager.Licensing
{ {
foreach (ManagementObject mo in moc) foreach (ManagementObject mo in moc)
{ {
pcInfo = mo.Properties["processorID"].Value.ToString(); pcInfo = mo.Properties["processorID"]?.Value?.ToString() ?? string.Empty;
break; break;
} }
} }
@ -23,7 +23,7 @@ namespace NcProgramManager.Licensing
{ {
foreach (ManagementObject obj in searcher.Get()) foreach (ManagementObject obj in searcher.Get())
{ {
pcInfo += obj["SerialNumber"].ToString(); pcInfo += obj["SerialNumber"]?.ToString() ?? string.Empty;
break; break;
} }
} }