Skip to content

Add auditable Sampled Values evidence bundles - #109

Closed
masarray wants to merge 7 commits into
agent/p0-hardening-1-6-19from
agent/p1-sv-evidence-bundle
Closed

Add auditable Sampled Values evidence bundles#109
masarray wants to merge 7 commits into
agent/p0-hardening-1-6-19from
agent/p1-sv-evidence-bundle

Conversation

@masarray

Copy link
Copy Markdown
Owner

Purpose

Implement ARSAS P1 as a single auditable Sampled Values Evidence Bundle that can be stored, transferred, compared, and reviewed after a bounded passive capture.

Evidence bundle

The SMV Snapshot Viewer now exports one ZIP containing:

  • waveform.png — rendered static raw-lane waveform
  • samples.csv — sample index, reconstructed displayed smpCnt sequence, and every plotted raw lane
  • manifest.json — schema, PASS/REVIEW verdict, stream identity, capture evidence, channel metadata, and explicit evidence boundary
  • diagnostics.txt — continuity counts, timebase, payload shape, parser diagnostics, and unresolved-semantic boundary
  • SHA256SUMS.txt — SHA-256 for every evidence entry

The completed ZIP also receives a bundle-level SHA-256 shown to the operator.

Provenance

  • application version and informational build identity
  • embedded immutable engines/ARIEC61850.lock.json
  • engine repository, ref, and exact commit
  • immutable stream selection used to own the capture

Engine commit remains 0f8453182957900bc6d91287fb8177c8d9762188.

Safety and UX

  • export is enabled only after an accepted snapshot exists
  • export stays disabled during active capture
  • export never transmits or changes an IEC 61850 frame
  • bundle verdict uses the canonical restart-aware SmvSnapshotResult.IsCleanProof
  • raw lanes remain explicitly semantically unresolved until trusted ordered SCL mapping and reviewed scaling are bound
  • files are written through a temporary path and atomically moved only after ZIP completion

Regression validation

Adds an application test that creates a real ZIP, opens it, and verifies:

  • exact required entry names
  • manifest schema, REVIEW verdict, app version, and engine commit
  • raw CSV samples
  • restart diagnostics
  • per-entry checksum listing

Stack boundary

This PR is intentionally based on P0 branch agent/p0-hardening-1-6-19 / PR #107. It contains only the P1 delta. Stable public release remains 1.6.18.

@masarray
masarray marked this pull request as ready for review July 28, 2026 06:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6407ece174

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for (var sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
{
builder.Append(sampleIndex.ToString(CultureInfo.InvariantCulture));
var smpCnt = (snapshot.FirstSampleCount + sampleIndex) % wrap;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the actual captured smpCnt values in the CSV

When FirstSampleCount is outside the two-cycle window size, this immediately writes a false counter value—for example, a snapshot starting at 1000 with 80 samples/cycle emits 40 because it takes modulo 160, contradicting the manifest's firstSampleCount. It also invents a continuous sequence for REVIEW captures containing gaps, duplicates, out-of-order samples, or restarts. Since the snapshot currently retains only the first and last counters, preserve each ASDU's actual smpCnt during capture and export that sequence alongside its raw sample.

Useful? React with 👍 / 👎.

Comment on lines +55 to +58
private void RefreshEvidenceBundleButton()
{
if (_exportEvidenceButton is not null)
_exportEvidenceButton.IsEnabled = _snapshot is not null && !CancelButton.IsEnabled;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh export availability after capture ends

On the normal successful-capture path, ApplySnapshot changes SnapshotChannels before Capture_Click reaches its finally and calls SetCaptureState(false), so every collection-triggered refresh sees CancelButton.IsEnabled == true and leaves this button disabled. The final transition that disables Cancel does not call RefreshEvidenceBundleButton, and there is no subsequent collection change, so an accepted snapshot cannot be exported through the new UI.

Useful? React with 👍 / 👎.

Comment on lines +137 to +139
capture = new
{
snapshot.NominalFrequencyHz,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record the capture timestamps in the manifest

When a snapshot is exported later or exported more than once, exportedAtUtc identifies only when the ZIP was written; the available SmvSnapshotResult.StartedAt and CompletedAt values are omitted from the capture evidence. Consequently, a reviewer cannot establish when the represented network observation occurred or distinguish otherwise identical captures, which undermines the bundle's audit trail.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Closing this PR as superseded by #108. Both implement the same stacked P1 Sampled Values evidence-bundle scope on top of #107, but #108 has the broader audited bundle, dedicated validation workflow, deterministic regression coverage, and all review findings resolved. This PR still has unresolved correctness findings around exported smpCnt evidence, export-button state, and capture timestamps. Keeping one canonical P1 path avoids duplicate exporters and overlapping edits to the SMV window, project metadata, tests, and changelog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant