Commit Graph

12 Commits

Author SHA1 Message Date
Joseph Doherty 5d2386cc9d fix(transport): close bundle security + plaintext-retention gaps (4 findings)
T-003: move the unlock lockout server-side. The 3-strike counter used to
live in the Razor page only — a second tab / CLI caller could re-upload
the same bytes and grind PBKDF2 indefinitely. The counter now lives in
IBundleSessionStore, keyed by ContentHash, so retries against identical
bundle bytes are throttled regardless of client. BundleLockedException
surfaces the new typed error path.

T-005: bind the manifest's non-derivative fields into AES-GCM AAD. A
SHA-256 of the manifest (with ContentHash + Encryption normalised to
sentinels) is now passed to AesGcm.Encrypt / .Decrypt, so a tampered
SourceEnvironment / ExportedBy / CreatedAtUtc on a stolen bundle yields
an authentication-tag mismatch instead of slipping past the Step-4
typo-resistant confirmation gate.

T-006: cap zip entry count, decompressed length, and compression ratio
in LoadAsync's envelope validator BEFORE any payload is decompressed,
using ZipArchiveEntry.Length / .CompressedLength. New TransportOptions
fields default to 4 entries / 200 MB / 50x ratio.

T-007: clear decrypted plaintext on the ApplyAsync failure path and zero
the buffer on success before removing the session, so a 100 MB
DecryptedContent doesn't sit in memory for the 30-min TTL after a failed
apply. A BundleSessionEvictionService BackgroundService now also drives
EvictExpired periodically so abandoned sessions clear without needing a
fresh Get() call to trigger lazy eviction.

Also resolves NO-010 — the misleading "writer never throws" XML doc was
the same code+comment my prior NO-004 await-the-writer fix already
rewrote.
2026-05-28 04:14:07 -04:00
Joseph Doherty bae75be2d2 fix(transport): stop scanning DataSourceReference for blocker references
DetectBlockersAsync was feeding TemplateAttribute.DataSourceReference
into the identifier scanner alongside script bodies, but that field is
an OPC UA node-address path (e.g. "ns=3;s=Tank.Level") owned by the
device, not script source. The dot delimiter inside the path tripped
the heuristic into flagging the address segment ("Tank", "Sensor",
"TestChildObject", "DevAppEngine") as a missing SharedScript or
ExternalSystem reference -- a 100% false-positive class on any
template catalog with OPC-UA-mapped attributes.

Drop the DataSourceReference scan entirely. Attribute.Value is still
scanned because it can carry a design-time default expression that
calls into runtime APIs. Add a regression test pinning the new behavior.
2026-05-24 07:52:31 -04:00
Joseph Doherty 6bdada7549 fix(transport): drop blocker false positives for stdlib + member access
The DetectBlockersAsync heuristic was catching every PascalCase
"Identifier(" or "Identifier." token in script bodies and treating it
as a candidate SharedScript or ExternalSystem reference. On a normal
template catalog this surfaced 30+ blocker rows for .NET stdlib
(DateTimeOffset, Convert, ToString, Dispose, UtcNow...), ScadaLink
runtime API roots (Notify, Database, ExternalSystem, Scripts...), and
SQL keywords inside string literals (COUNT), blocking the import.

Two surgical fixes:

1. Skip identifiers preceded by `.` so `obj.Method()` no longer flags
   `Method` as a top-level reference.
2. Maintain a `KnownNonReferenceNames` denylist for the small set of
   well-known stdlib / runtime / SQL tokens that can never be
   user-defined SharedScripts or ExternalSystems.

The documented use case -- a top-level free-standing call to a missing
SharedScript or ExternalSystem (e.g. `MissingHelper()` at the start of
an expression, or `ErpSystem.Call(...)` where ErpSystem is the
external-system identifier) -- still produces a blocker row, pinned
by the existing test plus a new noise-filter regression test.
2026-05-24 07:46:24 -04:00
Joseph Doherty 624cf255a4 feat(transport): wire full SemanticValidator at bundle import time 2026-05-24 06:32:42 -04:00
Joseph Doherty 8e73e60f4a feat(transport): restore composition + alarm-script edges on bundle import 2026-05-24 06:16:24 -04:00
Joseph Doherty 9f1bb81993 test(transport): integration conflict resolution + rollback 2026-05-24 05:50:11 -04:00
Joseph Doherty 623aa8d061 test(transport): integration round-trip export → wipe → import 2026-05-24 05:48:24 -04:00
Joseph Doherty cda80cf821 fix(transport): robust failure-audit when rollback throws + doc clarifications
Address one Blocker and three Important findings from code review of
2c34f12 (BundleImporter.ApplyAsync):

- BLOCKER: wrap RollbackAsync in nested try/catch so a rollback fault
  does not swallow the BundleImportFailed audit row. Dispose the
  failed transaction before the audit-write so the new SaveChangesAsync
  uses a fresh implicit transaction instead of enlisting in the broken
  one. Surface the rollback exception's message on the failure row
  alongside the original cause, and swallow audit-write faults per the
  design's best-effort-audit invariant. Add regression integration
  test using a SQLite transaction interceptor that throws on rollback.

- Document re-entrancy assumption on IAuditCorrelationContext: scoped
  lifetime, single circuit, concurrent imports within a shared scope
  must serialize externally.

- Document repository audit responsibility on BundleImporter: repos
  are thin EF wrappers; ApplyAsync writes audit rows explicitly. If
  repos ever start emitting audit rows, the explicit calls here must
  be removed to avoid double-logging.

- Document BundleSessionStore thread-safety: ConcurrentDictionary
  primitives are safe under concurrent callers; BundleSession itself
  is not thread-safe.
2026-05-24 05:06:04 -04:00
Joseph Doherty 2c34f12a6f feat(transport): BundleImporter.ApplyAsync transactional with audit correlation 2026-05-24 04:55:43 -04:00
Joseph Doherty 2400249453 feat(transport): BundleImporter.PreviewAsync diff engine 2026-05-24 04:41:24 -04:00
Joseph Doherty 7c70ce0dbf feat(transport): BundleExporter with audit logging 2026-05-24 04:30:18 -04:00
Joseph Doherty 7e51274812 feat(transport): scaffold ScadaLink.Transport project + test projects 2026-05-24 03:57:07 -04:00