Skip to content

CTT conformance: access rights, X509, aggregates, and matrix diagnostics#3978

Merged
marcschier merged 26 commits into
masterfrom
copilot/ctt-run5-fixes
Jul 13, 2026
Merged

CTT conformance: access rights, X509, aggregates, and matrix diagnostics#3978
marcschier merged 26 commits into
masterfrom
copilot/ctt-run5-fixes

Conversation

@marcschier

@marcschier marcschier commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Description

Investigates successive OPC UA CTT runs and fixes the proven server conformance defects while documenting proven CTT script/codec defects in plans/ctt-issues.md.

Server fixes

  • Grant WriteAttribute on Scalar_Static_Int32 so attributes advertised by WriteMask are writable.
  • Disable the client-certificate-keyed authentication lockout in the dedicated CTT configuration so negative token tests do not mask later results with BadUserAccessDenied.
  • Map an X509 user-token signature algorithm mismatch to token-level BadIdentityTokenInvalid.
  • Persist rejected X509 user certificates to pki/rejectedUser to simplify trusted-user provisioning.
  • Populate HistoricalDataConfiguration.AggregateConfiguration with the server's actual defaults.
  • Return per-node BadInvalidArgument for processed reads with equal StartTime/EndTime.
  • Feed reverse processed reads in request order and preserve exact non-Bad interpolation boundaries.
  • Correct PercentGood/PercentBad uncertain classification.
  • Correct WorstQuality/WorstQuality2 eligible bounds, StatusCode preservation, and MultipleValues.
  • Correct reverse Min/Max Raw-vs-Calculated semantics.
  • Fix HistoryClient.GetConfigurationAsync traversal and aggregate-configuration reads.

Tests and CTT evidence

  • Added direct calculator and live historian Part 11/13 oracle tests covering forward/reverse intervals, equal-time validation, bounds, status aggregates, DurationInState, and Min/Max.
  • Added exact Part 6 Table 26 binary golden-vector coverage for a 2×3 Int32 Matrix Variant.
  • Added in-process whole-matrix read/write and multidimensional NumericRange coverage.
  • Updated plans/ctt-issues.md with exact evidence and recommended fixes for CTT aggregate, X509, multidimensional-array, A&C, and AliasName script defects.

No CTT failure is suppressed or accepted without a spec-based classification.

Related Issues

Checklist

  • Tests proving the server fixes are included.
  • CTT-side defects are documented with script locations and specification references.
  • Targeted aggregate/routing/matrix suites pass on net48 and net10.
  • History aggregate/client suites pass on net48 and net10.
  • Modified lines are analyzer-clean.
  • CI and CodeQL complete.
  • Maintainer review complete.

…ess Space WriteMask)

The static Int32 test node carries explicit RolePermissions granting Browse|Read|Write|ReadHistory|ReadRolePermissions but not WriteAttribute. The CTT Address Space WriteMask unit writes the non-Value attributes the node's WriteMask advertises (DisplayName/Description); those require PermissionType.WriteAttribute. Because RolePermissions are enforced for every attribute once present, the authenticated user lacking WriteAttribute got BadUserAccessDenied (126 failures) instead of letting the per-attribute WriteMask decide Good/BadNotWritable. Added WriteAttribute to kTestNodePermissions so the WriteMask governs the result. Added AddressSpaceWriteMaskTests regression (write Description must not be BadUserAccessDenied). Green net48+net10.
…ings

Security User X509 cluster (29): the SessionManager brute-force lockout
(MaxFailedAuthenticationAttempts, default 5, keyed on the shared client
application-instance certificate thumbprint) trips partway through the CTT's
X509 negative-token suite - every rejected attempt counts, even the ones the
CTT expects to fail - so from test 005 on every ActivateSession returns
BadUserAccessDenied, masking the real per-token result (incl. the positive
011/013 cases that expect Good). Lockout is vendor hardening, not an OPC UA
conformance requirement, so disable it for compliance testing via
MaxFailedAuthenticationAttempts=0 in Ctt.ReferenceServer.Config.xml (production
keeps the default 5). Behaviour proven by
ClientLockoutTests.ClientIsNotLockedOutWhenLockoutDisabledAsync.

plans/ctt-issues.md: add CTT-script defect #5 (HAAggregateHelper.js:1484
dereferences possibleNodeId without the isDefined guard used elsewhere) and a
run5 findings section (X509 lockout, cert-trust provisioning for valid-cert
037/044/051/052, and the aggregate server-vs-CTT value comparison that needs
the CTT loop to attribute).
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.05128% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.89%. Comparing base (2a1db65) to head (08a9894).

Files with missing lines Patch % Lines
...er/Historian/InMemory/InMemoryHistorianProvider.cs 76.56% 9 Missing and 6 partials ⚠️
...ies/Opc.Ua.Server/Historian/HistorianDispatcher.cs 69.44% 6 Missing and 5 partials ⚠️
.../Historian/HistoricalDataConfigurationInstaller.cs 37.50% 0 Missing and 5 partials ⚠️
...es/Opc.Ua.Client/Historian/HistoryClient.Extras.cs 95.23% 0 Missing and 2 partials ⚠️
....Ua.Server/Aggregates/StatusAggregateCalculator.cs 91.66% 0 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3978      +/-   ##
==========================================
- Coverage   79.22%   78.89%   -0.33%     
==========================================
  Files        1391     1391              
  Lines      183862   184025     +163     
  Branches    32070    32115      +45     
==========================================
- Hits       145661   145188     -473     
- Misses      26761    27443     +682     
+ Partials    11440    11394      -46     
Files with missing lines Coverage Δ
...es/Opc.Ua.Server/Aggregates/AggregateCalculator.cs 79.06% <100.00%> (+5.92%) ⬆️
....Ua.Server/Aggregates/MinMaxAggregateCalculator.cs 80.09% <100.00%> (+3.39%) ⬆️
....Ua.Server/Historian/HistorianContinuationState.cs 100.00% <ø> (ø)
...c.Ua.Server/Historian/HistorianNodeCapabilities.cs 100.00% <100.00%> (ø)
...pc.Ua.Core/Stack/Types/X509IdentityTokenHandler.cs 67.03% <100.00%> (+1.91%) ⬆️
...es/Opc.Ua.Client/Historian/HistoryClient.Extras.cs 92.12% <95.23%> (+0.74%) ⬆️
....Ua.Server/Aggregates/StatusAggregateCalculator.cs 87.50% <91.66%> (+4.68%) ⬆️
.../Historian/HistoricalDataConfigurationInstaller.cs 85.71% <37.50%> (-6.23%) ⬇️
...ies/Opc.Ua.Server/Historian/HistorianDispatcher.cs 79.44% <69.44%> (-0.25%) ⬇️
...er/Historian/InMemory/InMemoryHistorianProvider.cs 68.79% <76.56%> (+0.36%) ⬆️

... and 54 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@marcschier
marcschier marked this pull request as ready for review July 9, 2026 18:18
Copilot AI review requested due to automatic review settings July 9, 2026 18:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves OPC UA CTT (Compliance Test Tool) conformance for the reference server by fixing an access-rights gap affecting non-Value attribute writes governed by WriteMask, disabling authentication lockout in the dedicated CTT server config to avoid masking X509-token results, and documenting identified CTT-side script defects and run findings.

Changes:

  • Add a regression test ensuring WriteMask-writable non-Value attribute writes are not rejected as BadUserAccessDenied.
  • Update the reference server conformance node’s explicit RolePermissions to include PermissionType.WriteAttribute.
  • Disable MaxFailedAuthenticationAttempts in Ctt.ReferenceServer.Config.xml and document remaining CTT-side defects/findings in plans/ctt-issues.md.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
Tests/Opc.Ua.InformationModel.Tests/AddressSpaceWriteMaskTests.cs Adds a regression test to prevent BadUserAccessDenied for WriteMask-writable non-Value attribute writes.
plans/ctt-issues.md Documents an additional CTT script defect and summarizes “latest run” residual findings/attribution notes.
Applications/Quickstarts.Servers/ReferenceServer/ReferenceNodeManager.cs Extends explicit RolePermissions for Scalar_Static_Int32 to include WriteAttribute so WriteMask governs non-Value writes.
Applications/ConsoleReferenceServer/Ctt.ReferenceServer.Config.xml Disables auth lockout for CTT runs to prevent false BadUserAccessDenied cascades in negative-token suites.

Comment thread Applications/Quickstarts.Servers/ReferenceServer/ReferenceNodeManager.cs Outdated
@marcschier marcschier linked an issue Jul 9, 2026 that may be closed by this pull request
…nt32

The comment enumerated Browse+Read+Write+ReadHistory+ReadRolePermissions
(omitting the newly added WriteAttribute) and framed WriteAttribute as a
SecurityAdmin-only concern. WriteAttribute is part of kTestNodePermissions and
is granted to Anonymous and AuthenticatedUser; SecurityAdmin simply gets the
full permission set. Comment-only change, no behavior change.
…store

Investigates the X509-only CTT re-run (Security User X509, 17 errors) taken
after the auth-lockout fix.

Server fix (CTT 018): a user-token signature built with a wrong algorithm
returned the channel-level BadSecurityChecksFailed. SecurityPolicies.
VerifySignatureData (shared with the channel signature checks) throws
BadSecurityChecksFailed for an unexpected SignatureData.Algorithm, and
X509IdentityTokenHandler.VerifyAsync propagated it unchanged. Map that
algorithm-mismatch BadSecurityChecksFailed to the token-level
BadIdentityTokenInvalid in the user-token path (Part 4), leaving the shared
channel-signature behaviour untouched. Regression test added.

Reference server enhancement (ease provisioning): the remaining 16 errors are
the CTT's trusted user certs not being in the server's pki/trustedUser store
(the server correctly rejects untrusted user certs per Part 4). The reference
server now persists every rejected X509 user certificate to a dedicated
pki/rejectedUser review store (sibling of pki/trustedUser) so an operator can
move the legitimate ones into the trusted-user store and re-run. Also refactors
VerifyX509IdentityToken to async (removing the sync-over-async validation call).
Integration test + README note added.

plans/ctt-issues.md: document CTT-script defect #6 (the '// to prevent user
lockout' cleanup ActivateSession lacks error suppression, so it can fail an
otherwise-passing negative test - UaR.js:219) and the run6 provisioning /
rejectedUser workflow, with OPC UA spec references.

Validated on net48 + net10 (X509IdentityTokenHandlerTests, SecurityX509UserTests).
@marcschier marcschier changed the title CTT run5: fix Scalar_Static_Int32 WriteAttribute + disable auth lockout in CTT config; document CTT defects CTT conformance fixes: WriteAttribute access rights, auth-lockout, X509 user-token error code + rejected-user review store Jul 10, 2026
marcschier and others added 13 commits July 10, 2026 10:01
Root-causes the 6016-error aggregate cluster using the CTT console log
(Server Value / CTT Value diagnostics). Under UseServerCapabilitiesDefaults the
CTT reads the node's HistoricalDataConfiguration -> AggregateConfiguration object
and computes aggregates with those values, then compares to the server's
ReadProcessed. The server computes with PercentDataGood/Bad=100,
TreatUncertainAsBad=true (a partial no-data interval => Uncertain), but
HistoricalDataConfigurationInstaller.PopulateProperties materialised the
mandatory AggregateConfiguration child WITHOUT populating it, leaving the
type's all-zero defaults (0/0/false) - both inconsistent with the server's
computation and an invalid AggregateConfiguration (PercentDataGood <
100-PercentDataBad). A CTT reading 0/0/false classifies every partial interval
as Bad while the server returns Uncertain -> the systematic divergence across
every aggregate/node.

Fix: HistorianNodeCapabilities gains DefaultAggregateConfiguration (defaulting
to the Part 13 v1.05.07 4.2.1.2 defaults 100/100/true/false); the installer
populates the AggregateConfiguration object from it, so a
UseServerCapabilitiesDefaults client reads the same config the server computes
with. Also fixes a latent client bug: HistoryClient.GetConfigurationAsync
resolved the companion object via HasAddIn instead of HasHistoricalConfiguration
(Part 11 5.2.3) and never read AggregateConfiguration; it now does both.

Regression: HistoryClientIntegrationTests.GetConfigurationReturnsHistoricalData
ConfigurationAsync asserts the advertised PercentDataGood/Bad=100,
TreatUncertainAsBad=true. plans/ctt-issues.md documents the root cause + the
remaining CTT-script items. Validated net48 + net10.
Alarms & Conditions (152): using the console log (ctt output 1.txt) - the
'After Acknowledge Retain in invalid state' (42) is spec-correct server
behaviour: core GetRetainState returns false for an inactive+acked+confirmed
condition with no branches and UpdateRetainState precedes ReportStateChange, so
Retain=true reflects an outstanding condition branch (Part 9 5.8.2/5.7.3) that
the CTT's ValidateRetain does not account for. ConditionDisabled (60) and
Unable-to-read-input (42, AnalogSource is a readable variable) need the live
A&C loop to attribute.

AliasName (12): the reference server is FindAlias-only (InMemoryAliasNameStore)
and does not materialise browsable AliasNameType instance nodes under the
categories, so the CTT Category/Hierarchy units (which browse for instances)
report 'No instance found' / 'not part of the current category'. Documented as
Part 17 profile-dependent (expose instances vs gate the assertion). Plus a
genuine CTT-script defect: AliasName Hierarchy/002.js:80 references undefined
TC_Variables.ListOfNodes (should be OutputArguments).

All with Part 9 / Part 17 references for the CTT developers.
# Conflicts:
#	Applications/Quickstarts.Servers/ReferenceServer/ReferenceServer.cs
…BadInvalidArgument)

Per OPC 10000-11 (Part 11) v1.05.07 6.5.4.2, a processed history read whose StartTime equals EndTime has a zero-width time domain with no meaningful interpretation, so the Server shall return Bad_InvalidArgument. The server previously computed and returned Good, which CTT flags in the aggregate multi-node cluster (aggregate_002_01: 'Server Status [Good] not equal to ctt status [BadInvalidArgument]', and the single-node aggregate_001_01), both driven by the StartEqualsEnd time definition in HAAggregateHelper.PerformMultipleNodeTest / PerformSingleNodeTest.

Add a narrow guard at the top of HistorianDispatcher.DispatchProcessedReadAsync that returns Bad_InvalidArgument per operation (matching CTT, which expects the HistoryRead service call to succeed with per-node BadInvalidArgument results rather than a service fault). Covers both async and sync custom node manager read paths.

Tests (net10 + net48): faithful multi-node service-path reproduction (HistoryReadProcessedWithEqualStartAndEndTimeReturnsBadInvalidArgumentPerNodeAsync) plus dispatcher-level equal-time and reverse-order guard tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Feed reverse processed history reads in request order, preserve exact raw endpoints during interpolation, and honor uncertain classification and repeated Good quality semantics.

Add direct calculator and live historian Part 11/13 oracle coverage for focused CTT aggregate scenarios.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add deterministic Part 6 matrix wire and server NumericRange tests, and document the run12 CTT failure cascades and script defects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover Good, Uncertain, and Bad raw values exactly on a reverse interpolative slice boundary, including value, status bits, and timestamps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Return exact non-Bad raw values for Interpolative aggregates regardless of TreatUncertainAsBad, while continuing to interpolate across exact Bad points. Cover forward and reverse Good, Uncertain, and Bad boundaries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include only the request start bound for WorstQuality2 and preserve the first status code at the worst severity in request order. Add forward, reverse, and mixed-status coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Minimum/Maximum (and Minimum2/Maximum2) marked a result Calculated whenever the
selected raw sample timestamp differed from the chronological lower slice bound.
For reverse reads (startTime > endTime) the request-direction interval start is
the later timestamp, so a min/max sample sitting on the returned interval
timestamp was wrongly flagged Calculated instead of Good, Raw
(Part 13 5.4.3.10/5.4.3.11, interval geometry Part 13 5.4.2.2 / Part 11).

Compare the selected sample against GetTimestamp(slice) (the request-direction
interval start) in both ComputeMinMax and ComputeMinMax2. Forward reads are
unchanged because GetTimestamp(slice) == slice.StartTime there.

Add independent forward/reverse oracle tests for Minimum/Maximum, Minimum2/
Maximum2 and the reverse ActualTime variants, and correct the shared CTT
regression oracle that encoded the buggy reverse-Maximum expectation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@marcschier marcschier changed the title CTT conformance fixes: WriteAttribute access rights, auth-lockout, X509 user-token error code + rejected-user review store CTT conformance: access rights, X509, aggregates, and matrix diagnostics Jul 11, 2026
marcschier and others added 5 commits July 12, 2026 00:53
Correct one-sided and equal-time reads, enforce modified-read bound validation, count and synthesize Part 11 bounds correctly, and make history continuation points single-use. Add focused unit and integration coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reproduce the reference server's all-Good linear-ramp seed (SeedHistoricalNodeAsync) and pin the server's Part 13 aggregate behaviour for the residual value families reported by CTT run 14. Direct and live in-memory historian reads confirm the server is spec-correct and the residual mismatches are on the CTT oracle side:

- Float/Double sloped interpolation bounds (Interpolative/StartBound/EndBound) are byte-exact; CTT only diverges on integer-typed nodes.

- Integer interpolation rounds to nearest via Convert.ToInt32 (identical in 1.5.378/master378 and HEAD); Part 13 5.4.3.2.2 does not mandate truncation, so the CTT oracle flooring to the earlier integer is a convention difference, not a server regression.

- Duration/Percent/WorstQuality2/DurationInState over all-Good data are full-Good; the CTT expectations assume a configured Bad-data region absent from the all-Good seed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
marcschier and others added 4 commits July 12, 2026 15:00
Return the next bound for equal-time exact reads, and synthesize footnote LAST/FIRST bounds when one-sided reads exhaust available history before NumValuesPerNode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@marcschier
marcschier merged commit 0da85b9 into master Jul 13, 2026
164 checks passed
@marcschier
marcschier deleted the copilot/ctt-run5-fixes branch July 15, 2026 06:21
marcschier added a commit that referenced this pull request Jul 18, 2026
…lidation (#4002)

# Description

Follow-up to #3978 based on the latest OPC UA CTT aggregate, matrix,
Historical Access, Attribute, and Node Management runs.

## Aggregate run 16

- Ignore synthetic `BadBoundNotFound` raw-bound markers when feeding
aggregate calculators.
- Add direct and live historian regression coverage.

## Multi-dimensional Variant matrices

- Fix `Variant.Copy()` so rank-2 Variant matrices preserve their payload
and dimensions.
- Enforce valid Variant matrix dimensions consistently across Binary,
JSON, XML stream, and XML parser codecs.
- Encoders return `BadEncodingError`; decoders return `BadDecodingError`
for malformed matrix dimensions.
- Add cross-codec valid/null/empty/malformed tests and a batched
ReferenceServer Read regression.
- Correct the previous CTT issue attribution: the invalid
`Scalar_Static_Arrays2D_Variant` server encoding poisoned the batched
CTT Read.

## Node Management AddNodes

- Return `BadNodeIdRejected` for unsupported requested-NodeId
namespaces.
- Validate hierarchical ReferenceType source/target NodeClass
constraints.
- Document run-18 confirmation that all remaining 15 AddNodes errors are
CTT project/script configuration issues.

## Historical Access and Attribute Services run 18

- Reject whitespace in `NumericRange` syntax with
`BadIndexRangeInvalid`.
- Add HistoryRead tests for operation-level invalid syntax and
per-DataValue `BadIndexRangeNoData`.
- Document CTT defects:
- `012.js` expects `BadIndexRangeNoData` at the operation level instead
of on returned DataValues;
  - `Err-012.js` uses a non-historizing node for an access-denied test;
- generic Attribute array helpers omit NodeId[] and StatusCode[]
conversion paths.

## Base Information model validation run 19

- Run the CTT main session as `sysadmin` over Basic256Sha256
SignAndEncrypt so SecurityAdmin RolePermissions and EncryptionRequired
restrictions are both satisfied without weakening the server model.
- Preserve predefined well-known NodeIds while making generated dynamic
type and optional-child factories allocate compatible per-instance
NodeIds, remap internal references, and update references from the
owning subtree.
- Materialize dynamic RoleType properties through generated helpers and
keep role-child string NodeIds disjoint from numeric RoleManager root
IDs.
- Add net48/net10 regressions for generator output, NodeId
allocation/reference remapping, DI mandatory children, dynamic roles,
and low-privilege versus SecurityAdmin Browse visibility.
- Document CTT defects for permission-unaware mandatory-child
validation, the UA 1.04 reference model used against a UA 1.05 server,
stale ServerDiagnostics cache, and the `ConformanceUnits` scalar/array
mismatch.
## Related Issues

- Follow-up to #3960 and #3978.

## Checklist

- [x] I have signed the CLA and read the CONTRIBUTING document.
- [x] Tests prove the fixes and improve coverage.
- [x] Required CTT documentation is updated.
- [x] Focused builds/tests introduce no warnings.
- [x] Affected tests pass on .NET Framework and .NET 10.
- [ ] CI and CodeQL are green.
- [ ] All PR feedback is addressed.



## Latest CTT full run and A&C run 20

- Fix Monitor Value Change V2 `036.js`: an ambiguous
`DataValue(StatusCode)` call encoded `BadIndexRangeNoData` as a Good
value.
- Prove all 19 configured matrix monitored items notify when the
selected value actually changes; document the independent `042.js`
script defects.
- Rebase dynamic `CertificateExpired` / `TrustListOutOfDate` alarm roots
and descendants, restore standard type declarations and ModellingRules,
and expose the cross-node-manager references.
- Deliver method-triggered events to every authorized Session, fixing
the two-Session Confirm scenario.
- Preserve locale-only `LocalizedText` values consistently across
Binary, JSON, and XML and align Acknowledge/Confirm comment behavior and
tests.
- Document the remaining proven CTT defects for TransactionDiagnostics,
SemanticChange, History `013`, PolicyId scope, A&C, durable
subscriptions, and Subscription Minimum events.

## Base synchronization

Merged `master` through `007cfaf90` (`#4010`, repository layout
consolidation) and retained the matrix regression tests at their new
`tests/` locations.

## Latest validation

- Focused merged-tree tests pass on net10 and net48.
- `Opc.Ua.Server` and `Quickstarts.Servers` Release builds complete with
0 warnings and 0 errors on net10 and net48.
- The existing
`DisableEnableViaTypeAndInstanceMethodNoEventsDuringDisabledAsync`
timing test took its documented inconclusive timeout path after the
layout merge; no test failure was reported.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <romanett98@gmail.com>
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.

[CTT] Improve compliance & coverage in several areas

3 participants