Commit Graph

23 Commits

Author SHA1 Message Date
Joseph Doherty 9ab1c00265 fix(review): remediate re-review findings — DCL-029/InboundAPI-031/SiteRuntime-032/StoreAndForward-028 + Low doc/test
Fixes the 8 findings from the 2026-06-24 re-review (commit c42bb485), with a
regression test per Medium finding:

- DataConnectionLayer-029 (Med): HandleAlarmSubscribeCompleted now mirrors the
  tag-path re-check — if a feed is already stored for the source, release the
  redundant just-created subscription instead of overwriting + leaking the first
  one (the double-subscribe window DCL-023 reopened). +regression test.
- InboundAPI-031 (Med): remove WaitForAttribute's local 5s grace backstop (tighter
  than the CommunicationService Ask's timeout+IntegrationTimeout round-trip budget,
  so a slow-but-valid timed-out 'false' got cancelled into a 500). Link only the
  client-abort + explicit caller tokens; the lower layer owns the backstop. +test.
- SiteRuntime-032 (Med): derive the deployed count from an authoritative set of
  deployed config names (HashSet) instead of a map-presence-gated int, so deleting
  a DISABLED instance decrements correctly (SiteRuntime-029's gate leaked it).
  +deploy->disable->delete regression test.
- StoreAndForward-028 (Med): reset _bufferedCount in StopAsync alongside the
  register-guard so a same-instance Stop->Start re-seeds from a clean base (no ~2N
  gauge double-count). +restart regression test.
- AuditLog-017 (Low): test the OnIngestAsync scope-resolution guard (actor survives,
  replies empty, counts the failure) — no longer unpinned.
- CentralUI-037 / ScriptAnalysis-009 / SiteRuntime-033 (Low): doc-comment + spec
  fixes (Database-throws in the inbound sandbox; baseReferences param wording;
  native-alarm cap return-to-normal + per-condition NativeAlarmDropped eviction).

Targeted suites green: SiteRuntime 5, StoreAndForward 6, InboundAPI 31,
DataConnectionLayer 10, AuditLog 5, ScriptAnalysis 40, CentralUI ScriptAnalysis 52.
2026-06-24 09:39:14 -04:00
Joseph Doherty b3c9014379 fix(inbound): authorize+secure Database helper, async/deadline-bound DB, wait-timeout-bound WaitForAttribute
Resolves InboundAPI-026/027/028/029 (+ newly-surfaced -030).

- 026: authorize the scoped Database helper in the design doc; SQL-injection
  protection is parameter binding (values never concatenated); allow writes via
  ExecuteAsync; drop the false 'read-only' claim. Named connections only.
- 027: async ADO.NET end-to-end (no .GetAwaiter().GetResult()); honour the method
  deadline token on ExecuteScalarAsync/ExecuteReaderAsync/ExecuteNonQueryAsync +
  a CommandTimeout backstop derived from the method timeout.
- 028: negative-path tests (null-gateway, deadline cancellation, parameterization)
  + e2e Database + WaitForAttribute cases through the real endpoint.
- 029: WaitForAttribute is bounded by its WAIT timeout (per-wait CTS + client-abort
  + explicit token), NOT the method deadline (spec §6) — a long wait may outlive the
  method timeout; WithRequestAborted threads the raw client-abort token separately.
- 030: Central UI compile-surface mirrors (InboundScriptHost / SandboxInboundScriptHost)
  gained the Database member (drifted since the runtime helper was added) so the
  authorized async API type-checks at the design-time gate.
2026-06-23 22:00:17 -04:00
Joseph Doherty 2935c41bf7 fix(inbound-api): recursively coerce nested Object/List values to typed CLR (#55) 2026-06-19 02:02:10 -04:00
Joseph Doherty 71d5722692 fix(m9/T32b): resolve $ref in InboundAPI runtime validators (no deploy-passes/runtime-400); diamond test; ref-annotation message 2026-06-18 12:16:39 -04:00
Joseph Doherty af54c8ad11 merge: integrate WaitAsync/M5-audit (parallel session) with galaxy array-write + inbound-timeout fixes 2026-06-17 09:28:15 -04:00
Joseph Doherty 0f6da8a106 feat(inbound): routed Route.To().WaitForAttribute — contract + central path (spec §6) 2026-06-17 09:02:21 -04:00
Joseph Doherty d4ec84d5fb fix(inbound): log swallowed scope-creation failure + test scope disposal on script throw 2026-06-16 22:00:10 -04:00
Joseph Doherty daff1446d8 feat(inbound): expose read-only Database helper on InboundScriptContext 2026-06-16 22:00:10 -04:00
Joseph Doherty 16fc62bfa0 test(inbound): add namespace + Query() coverage for InboundDatabaseHelper 2026-06-16 22:00:10 -04:00
Joseph Doherty 90ac746fdc feat(inbound): read-only InboundDatabaseHelper for inbound scripts 2026-06-16 22:00:10 -04:00
Joseph Doherty a07ff28f10 feat(audit): M5.3 response-capture increments — request headers, ceiling-hits counter, per-method body opt-out (T7)
1. Request headers in Extra JSON (AuditWriteMiddleware): adds a `requestHeaders`
   object to the existing Extra JSON alongside remoteIp/userAgent; headers whose
   names appear in AuditLogOptions.HeaderRedactList (Authorization, X-Api-Key,
   Cookie, Set-Cookie by default) are replaced with "<redacted>" using
   OrdinalIgnoreCase matching — same policy as ScadaBridgeAuditRedactor.

2. AuditInboundCeilingHits counter: new IAuditInboundCeilingHitsCounter interface
   + NoOpAuditInboundCeilingHitsCounter default; AuditCentralHealthSnapshot
   implements the interface (Interlocked field, thread-safe) and exposes
   AuditInboundCeilingHits on IAuditCentralHealthSnapshot; AddAuditLog registers
   the NoOp default, AddAuditLogCentralMaintenance forwards to the snapshot;
   AuditWriteMiddleware accepts the counter as an optional ctor arg and increments
   it once per request where either the request or response body hit the cap.

3. Per-method SkipBodyCapture opt-out: adds SkipBodyCapture bool to
   PerTargetRedactionOverride; AuditWriteMiddleware consults the per-target
   override map at the start of InvokeAsync (before EnableBuffering) and, when
   set, skips body read + capture entirely — the audit row still emits with
   headers/metadata but null RequestSummary/ResponseSummary; truncation flags
   are also cleared so the ceiling-hits counter is not bumped for opted-out methods.
2026-06-16 21:23:07 -04:00
Joseph Doherty 784fee7b07 refactor(inboundapi): M3.4 ForbiddenApiChecker delegates to shared ScriptAnalysis validator 2026-06-16 19:35:43 -04:00
Joseph Doherty 1392fd144a test(inbound-api): X-API-Key review nits — whitespace-auth fallthrough test + dedupe + comment wording
- Add WhitespaceAuthorization_ValidXApiKey_Returns200: pins the IsNullOrWhiteSpace
  fall-through — a present-but-blank Authorization header is treated as absent so a
  valid X-API-Key still authenticates (200).
- Remove MissingBearer_Returns401 (added in 510559e): identical path to
  NeitherHeader_Returns401 (no Authorization + no X-API-Key → 401); keep the
  descriptively-named NeitherHeader variant.
- Change "legacy 'X-API-Key'" -> "alternate 'X-API-Key'" in EndpointExtensions.cs and
  the BuildPostWithApiKeyHeader/HappyPath doc comments to avoid implying Bearer is
  the older transport (Bearer was itself introduced by the prior auth re-arch).
2026-06-16 14:06:03 -04:00
Joseph Doherty 510559e1be feat(inbound-api): accept X-API-Key header as credential transport alongside Authorization: Bearer 2026-06-16 14:01:01 -04:00
Joseph Doherty 411d0c043b fix(inbound-api): M2.6 review nits — legacy required default, recursion depth guard, return-validator comment (#13)
- legacy flat-array "required":"false" (string) now treated as optional (matches migration)
- depth ceiling (32) on InboundApiSchema Parse/Validate recursion — guards against
  stack-overflow from a deeply-nested stored schema (Parse throws->400, Validate adds error)
- DocOptions.MaxDepth=128 so the application-level structural guard fires before the
  System.Text.Json reader ceiling (each schema level = ~3 JSON reader levels)
- comment the intentional ParameterValidator/ReturnValueValidator early-return asymmetry
- note intentional datetime->string legacy collapse in NormalizeType
- tests: legacy string-false optional, parse/validate depth ceiling, scalar return schema
2026-06-15 15:18:44 -04:00
Joseph Doherty 4b6187c853 feat(inbound-api): nested Object/List extended-type validation (#13)
Object/List parameters and return values were shape-validated only (object vs
array), with no field-level/nested type checks — type-wrong nested data passed
inbound validation and failed only at script runtime. Add recursive type
validation (declared Object field types, List element type, scalars at any depth)
with path-qualified errors, symmetric across ParameterValidator and ReturnValueValidator.

Both validators now parse the canonical JSON Schema definition format (the
Central UI / MigrateParametersToJsonSchema output) via a shared recursive engine,
Commons.Types.InboundApi.InboundApiSchema, instead of the legacy flat
[{name,type}] array which they could not even deserialize from migrated rows.
The legacy flat-array form is still accepted on read for transition safety.
Undeclared fields are rejected at every level (consistent with the existing
top-level unexpected-parameter rejection); a present-but-null value satisfies
any type, only absence of a required field is an error.
2026-06-15 15:04:28 -04:00
Joseph Doherty b3de8408fa feat(audit): ScadaBridge IAuditActorAccessor + wire audit Actor from Auth principal at authenticated emit sites (Phase 3) 2026-06-02 15:33:01 -04:00
Joseph Doherty db707bb0de feat(audit)!: ScadaBridge C3 — swap to canonical ZB.MOM.WW.Audit.AuditEvent across seams/emitters/DTO/redactor wiring; transitional 24-col storage shim (Task 2.5) 2026-06-02 12:37:50 -04:00
Joseph Doherty afa55981d5 feat(auth)!: ScadaBridge retire SQL Server ApiKey entity + ApprovedApiKeyIds + legacy hashing; EF migration RetireInboundApiKeyStore; re-issue runbook + CHANGELOG (re-arch C5/E) — BREAKING: X-API-Key -> Bearer sbk_, keys re-issued 2026-06-02 05:39:59 -04:00
Joseph Doherty 1fcc4f5c2b fix(auth): ScadaBridge inbound auth review fixes — scope-before-DB, pinned 403 body, pepper fail-fast, log category 2026-06-02 02:50:10 -04:00
Joseph Doherty a94558c289 feat(auth): ScadaBridge inbound API — adopt ZB.MOM.WW.Auth.ApiKeys verifier + Bearer + scope=method (re-arch A+B); additive, old path retired later 2026-06-02 02:40:18 -04:00
Joseph Doherty 782fb73015 feat(scadabridge): emit scadabridge.inbound_api.requests (by method) at inbound API entry 2026-06-01 17:03:10 -04:00
Joseph Doherty 7b0b9c7365 refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj,
namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated.
ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated.
SQL roles/logins, LDAP domains, CLI command name, and CLI config dir
(~/.scadalink → ~/.scadabridge) also renamed.

Build green; 5 Host.Tests fail awaiting SQL login rename in next commit.
Pre-existing StaleTagMonitor timing flakes unchanged.

Rename script committed at tools/rename-to-scadabridge.sh.
2026-05-28 09:37:45 -04:00