Skip to content

feat(cryptography): unified-canvas UX + PGP private-key resource toggle [STUD-80718]#588

Open
alexandru-petre wants to merge 12 commits into
developfrom
feature/STUD-80718-unified-ux-fixes
Open

feat(cryptography): unified-canvas UX + PGP private-key resource toggle [STUD-80718]#588
alexandru-petre wants to merge 12 commits into
developfrom
feature/STUD-80718-unified-ux-fixes

Conversation

@alexandru-petre

@alexandru-petre alexandru-petre commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Context

UiPath.Cryptography.Activities is the Cryptography activity package (Encrypt/Decrypt Text & File, Hash Text & File, and the PGP activities). Each activity has a runtime class (CodeActivity) and a design-time DesignPropertiesViewModel that drives the unified canvas — the property panel and card shown in Studio. Property placement there is controlled by each property's ViewModel Category/IsPrincipal/OrderIndex, kept in sync with the runtime [LocalizedCategory] attributes.

This PR implements the follow-up fixes from the STUD-80718 unified-canvas adoption review, plus polish items surfaced while doing so.

Problem Statement

The review found the Encrypt/Decrypt/Hash activities didn't fully follow the unified-canvas guideline: interop and encoding knobs were scattered across the primary Input section and Options; outputs weren't consistently ordered last; Overwrite sat outside Options on the file activities; and the PGP private key could only be given as a file path — unlike its sibling public key and the PgpSign activities, it had no path↔resource toggle. The IV nonce-reuse risk was also only surfaced after a value was entered. These affect automation authors configuring these activities in Studio.

Behavior Before This PR

Opening Encrypt Text in Studio: Text encoding sat under Input, Key encoding under Options, and the third-party knobs (Format, KeyFormat, IV, KDF iterations, AES key size) under Input alongside the primary fields; the Result output could render before the Options tail. On Encrypt/Decrypt File, Overwrite surfaced on the collapsed card rather than in Options. For PGP, the private key (PrivateKeyFilePath) was a plain string only — it could not be picked from project resources / a Storage Bucket. The IV field's nonce-reuse warning only appeared as a design-time validation once an explicit IV was set.

Behavior After This PR

Encrypt/Decrypt Text sections read Input → Advanced → Encoding → Options → Output: both encodings live in a dedicated Encoding section, the interop knobs in Advanced, and the output is ordered last. Overwrite renders in Options on Encrypt/Decrypt File (consistent with the PGP file activities). The PGP private key now offers a path↔resource toggle (PrivateKeyFile, IResource) via a designer menu action, mirroring PublicKeyFile and PgpSign; at runtime the file path is used first, falling back to the resource only when the path is empty. The IV property description now carries a short WARNING: about never reusing a (Key, IV) pair, in addition to the existing design-time validation.

Considered Use Cases

These all concern the new PGP private-key path↔resource toggle — the only change here with runtime impact (everything else is design-time placement) — so its correctness and backward-compatibility edges were the focus:

  • PGP encrypt without signing with a bound-but-unused PrivateKeyFile resource — must not resolve it (resolution is gated on SignData); covered by new regression tests.
  • PGP decrypt — private key is always required, so it resolves unconditionally.
  • Persisted/reopened workflows — the path↔resource toggle restores the previously-bound side (same PairedInputToggle behavior as the existing key/passphrase toggles).
  • Backward compatibility — PrivateKeyFile is a new optional [Browsable(false)] InArgument<IResource> with [DefaultValue(null)]; existing XAML that binds only PrivateKeyFilePath is unaffected.

Implementation

Category/placement changes are applied to both the ViewModel Category and the runtime [LocalizedCategory] attributes (the package keeps the classic grid and unified canvas consistent); this also resolved a pre-existing key-encoding layer mismatch (runtime Input vs ViewModel Options). The new private-key resource resolution mirrors the existing PublicKeyFile inline ToLocalResource().ResolveAsync().GetAwaiter().GetResult() pattern (these are synchronous CodeActivity classes) and is deliberately non-throwing so an unused key can't fail a non-signing encrypt. ActivitiesMetadata.json needs no change — its Properties arrays are empty and the ViewModel drives everything. Activity reference docs under Packaging/docs/activities/ were updated for the new key resource pairing and the IV caution.

Caveats / Potential Issues

  • The section/placement changes are design-time only (no cryptographic behavior change), but the exact visual rendering is best confirmed by opening the activities in Studio — that step can't be automated here.

How to Test

  1. dotnet build Activities/Activities.Cryptography.sln — clean.
  2. dotnet test Activities/Activities.Cryptography.sln — green (404 package + 123 API tests), including new reflection existence tests for PrivateKeyFile and two regression tests asserting a bound PrivateKeyFile resource is never resolved when SignData = false.
  3. In Studio, open Encrypt/Decrypt Text & File and confirm sections render Input → Advanced → Encoding → Options → Output, Overwrite under Options, and the private-key "Use file / Use file path" menu action.

Independently cross-validated with Codex (2 rounds, final verdict Green — no outstanding findings).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ

alexandru-petre and others added 11 commits July 7, 2026 16:39
Implements the three gaps recorded in the STUD=80718 unified-UX review for
UiPath.Cryptography.Activities:

- S1: move the third-party-interop knobs (Format, KeyFormat, Iv, KdfIterations,
  AesKeySize) out of the primary Input section into a new secondary "Advanced"
  section, applied to both the unified-canvas ViewModels and the runtime
  [LocalizedCategory] attributes so the two layers stay consistent.
- P1: add a path<->resource toggle (PrivateKeyFile IResource) for the PGP
  private key in Encrypt/Decrypt Text & File, mirroring PublicKeyFile and the
  PgpSign activities. The encrypt path resolves the resource only when SignData
  is enabled, so a bound-but-unused key never fails a non-signing encrypt.
- S2: order the Output property after Options in the Text activities.

Adds the Category_Advanced_Name and PrivateKeyFile resx/Designer entries, plus
reflection existence tests and non-signing regression tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
Completes the STUD=80718 S2 fix for the remaining two activities the review
flagged: KeyedHashText and KeyedHashFile assigned Result (Output) a lower
OrderIndex than ContinueOnError (Options), so Output could render before
Options. Assign Result last, matching the Encrypt/Decrypt activities.

Design-time ordering only; no runtime behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
KeyedHashText/KeyedHashFile were the only activities marking their output
(Result) IsPrincipal = true, which surfaced it in the collapsed canvas card
among the inputs instead of the Output section. Align with every other
Cryptography output (Encrypt/Decrypt Text & File) by setting IsPrincipal =
false, so the output renders in the Output section after Options.

Design-time only; no runtime behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
The encoding config was scattered across sections in the unified canvas: Text
(plaintext) encoding under Input, Key encoding under Options, and the interop
knobs under Advanced. Introduce a dedicated "Encoding" category for the two
encoding dropdowns (keeping Advanced for the interop knobs), applied across all
six activities with encoding options: Encrypt/Decrypt Text & File and Hash
Text & File.

Key encoding moves to Encoding on all six; Text encoding (Text activities only)
moves from Input to Encoding. Applied to both the unified-canvas ViewModel
Category and the runtime [LocalizedCategory] attributes, which also resolves a
pre-existing layer mismatch (runtime typed key-encoding was Input while the
ViewModel used Options).

Adds the Category_Encoding_Name resx/Designer entry. Design-time grouping only;
no runtime behavior change. Resulting Text sections: Input, Encoding, Advanced,
Options, Output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
EncryptFile/DecryptFile were the only file-writing activities that marked
Overwrite IsPrincipal = true (surfacing it in the collapsed canvas card) and
whose runtime [LocalizedCategory] was Input. Align them with PgpSignFile /
PgpClearSignFile / PgpGenerateKeys: IsPrincipal = false so it renders in the
Options section, and runtime category Input -> Options to match the ViewModel
and the other activities.

Design-time/metadata only; no runtime behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
The (Key, IV) reuse warning is only surfaced at design time when an explicit IV
is entered. Add a short form of it to the IV property description (Encrypt Text
and Encrypt File) so the risk is visible up front, before a value is filled in.

English resx only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
Tooltips render as plain text (no markdown), so emphasize the warning with a
warning glyph, uppercase "WARNING", and a blank line separating it from the
descriptive text. Applies to Encrypt Text and Encrypt File IV descriptions.

English resx only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
Emphasize the IV nonce-reuse warning with just an uppercase "WARNING" on its own
line (blank-line separated), removing the warning glyph. Encrypt Text / Encrypt File.

English resx only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
The warning after the colon is a complete sentence, so capitalize its first word
per Microsoft style. Encrypt Text / Encrypt File.

English resx only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
…and IV caution

Keep the activity reference docs complete and current with the branch's changes:
- Encrypt/Decrypt Text & File: note that PublicKeyFilePath and PrivateKeyFilePath
  each have a hidden IResource alternative (PublicKeyFile / PrivateKeyFile) selectable
  via a designer menu action — matching how the package documents the same pairing for
  InputFilePath and the PgpSign activities. (PrivateKeyFile is new on these activities;
  PublicKeyFile was a pre-existing doc gap.)
- Encrypt Text/File: add the (Key, IV) nonce-reuse caution to the IV description,
  mirroring the property tooltip.

Docs only; no code change. Category/section moves (Advanced, Encoding, Overwrite->Options,
output ordering) need no doc changes — the docs group by argument kind, not designer
category. coded-api.md and the other docs are unaffected by this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
…ltips

Codex validation flagged that the PublicKeyFile / PrivateKeyFile descriptions said
the resource "takes precedence over the file-path variant", but the runtime uses the
file path first and only falls back to the resource when the path is empty (and the
path/resource toggle also prefers the path side). The two are mutually exclusive in
the designer anyway, so the precedence detail is dropped from all 8 descriptions
(PublicKeyFile + PrivateKeyFile across Encrypt/Decrypt Text & File).

English resx only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
@alexandru-petre alexandru-petre marked this pull request as ready for review July 8, 2026 16:19
@alexandru-petre alexandru-petre self-assigned this Jul 8, 2026
@alexandru-petre alexandru-petre changed the title [STUD-80718]: Unified-canvas UX refinements for Cryptography feat(cryptography): unified-canvas UX + PGP private-key resource toggle [STUD-80718] Jul 9, 2026
Collapse the duplicated PGP key path↔resource resolution blocks (public + private
key, across Encrypt/Decrypt Text & File) into a single PgpFileResolver.ResolveLocalPath
helper. This removes the copy-pasted blocks SonarCloud flagged as new-code duplication,
and concentrates the precedence logic (path-first, resource fallback, non-throwing) in
one unit-tested place.

The thin sync-over-async adapter that calls ToLocalResource() is isolated behind a
[ExcludeFromCodeCoverage] private method — it can't be exercised without the platform
resource stack — while ResolveLocalPath's precedence branches are covered directly by
new PgpFileResolverTests. Runtime behavior is unchanged (path still wins; resource used
only when the path is empty; encrypt still resolves the private key only when SignData).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn28po9dSpgQjnBU2Jh4yJ
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
50.0% Coverage on New Code (required ≥ 80%)
25.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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