You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implements GitHub Issue #390: GameProfile sharing via genhub://profile/import protocol, .ghprofile file packaging, and a rich dark acrylic pre-import inspection modal with manifest cache diffing and safety validation.
Motivation
Players and community creators need a frictionless, secure way to share curated game profiles (such as Generals Online competitive setups, Shockwave mods, map packs, resolution and audio configurations) directly via deep links (e.g. Discord, browsers) or exported files without manually setting up dependencies.
Changes
Core Architecture & Models:
Defined SharedGameProfilePackage, SharedProfileMetadata, SharedManifestDependency, SharedProfileInspectionResult, and SharedProfileImportRequest in GenHub.Core.Models.GameProfile.
Added protocol and command line constants (genhub://profile/import, --import-profile, .ghprofile) in CommandLineConstants and ProfileSharingConstants.
Implemented ProfileSharingCompressionHelper with Brotli compression, Base64Url codec, and shell injection token sanitization (|, &, ;, >, <, `, $).
Profile Sharing Service:
Implemented IProfileSharingService / ProfileSharingService handling URI generation, .ghprofile export/import, pre-import manifest diffing against local CAS cache, installation matching, name conflict resolution, and publisher factory resolution via PublisherManifestFactoryResolver.
Registered IProfileSharingService in GameProfileModule.
UI & Views:
Created ShareProfileDialogWindow.axaml & ShareProfileDialogViewModel (dark acrylic glass dialog for copying protocol URIs, copying Discord invite markdown, and exporting .ghprofile files).
Created ImportProfileInspectionWindow.axaml & ImportProfileInspectionViewModel (rich inspection modal showing icon/cover, itemized manifest breakdown with [✅ CACHED] vs [⬇️ Size] badges, installation selector, settings toggles, security alerts, and live progress overlay).
Integrated Share Profile action in GameProfileSettingsWindow header bar and GameProfileCardView context menu.
Wired startup arguments (--import-profile, genhub://profile/...) and single-instance IPC forwarding (import-profile:) in App.axaml.cs and GenHub.Windows/Program.cs.
Tests:
Added full test coverage in GenHub.Tests.Core for ProfileSharingService, ProfileSharingCompressionHelper, CommandLineParser, ShareProfileDialogViewModel, and ImportProfileInspectionViewModel.
Verification
All 1,815 unit tests in GenHub.Tests.Core executed and passing
undead2146
changed the title
feat(profiles): implement profile sharing via genhub:// protocol and rich inspection UI (#390)
feat(profiles): implement profile sharing via genhub:// protocol and rich inspection UI
Aug 19, 2026
Add secure game profile sharing and import inspection
✨ Enhancement🧪 Tests🕐 40+ Minutes
AI Description
• Adds portable profile packages and compressed genhub:// links for frictionless sharing.
• Inspects compatibility, cache state, downloads, naming conflicts, and launch safety before import.
• Adds sharing/import dialogs, startup routing, IPC forwarding, and comprehensive tests.
Diagram
sequenceDiagram
actor User
participant ShareUI as Share Dialog
participant Service as Sharing Service
participant Package as Shared Package
participant Startup as Startup Router
participant Inspector as Import Inspector
participant Cache as Manifest Pool
participant Repo as Profile Repository
User->>ShareUI: Share profile
ShareUI->>Service: Export profile
Service->>Repo: Load profile
Service->>Cache: Resolve manifests
Service->>Package: Encode URI or file
Package-->>ShareUI: Sharing artifact
User->>Startup: Open artifact
Startup->>Service: Inspect payload
Service->>Package: Decode and validate
Service->>Cache: Diff local cache
Service-->>Inspector: Inspection result
User->>Inspector: Confirm import
Inspector->>Service: Import request
Service->>Cache: Acquire dependencies
Service->>Repo: Save profile
Service-->>Inspector: Import result
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Signed hosted profile registry
➕ Provides publisher identity and package integrity verification.
➕ Avoids very large deep links and centralizes schema migration.
➕ Supports revocation, moderation, and reusable catalog identifiers.
➖ Requires backend hosting, authentication, and operational ownership.
➖ Introduces network dependence for otherwise portable profile sharing.
➖ Adds substantially more scope than the issue requires.
2. Orchestrator-only dependency acquisition
➕ Keeps downloads, verification, progress, and retries behind one established abstraction.
➕ Reduces direct handling of untrusted URLs and staging paths in the sharing service.
➕ Avoids synthesizing manifests when acquisition cannot find authoritative content.
➖ Cannot import dependencies absent from configured publishers.
➖ May require extending content search to resolve exact manifest IDs or embedded definitions.
➖ Reduces support for self-contained community packages.
Recommendation: The PR's local package plus compressed deep-link approach is appropriate for a frictionless first release and cleanly supports both online and offline exchange. Retain the current package/service boundary, but prefer orchestrator-managed acquisition wherever possible and consider package signatures or a hosted registry before treating remote community packages as trusted distribution artifacts.
Files changed (34) +3761 / -13
Enhancement (28) +2893 / -13
CommandLineConstants.csDefine profile-sharing protocol and CLI constants+55/-0
Define profile-sharing protocol and CLI constants
• Adds profile import and view URI paths, supported query parameters, and '--import-profile' argument forms for routing shared packages.
ProfileSharingCompressionHelper.csAdd profile payload codec and argument sanitization+154/-0
Add profile payload codec and argument sanitization
• Implements Brotli and Base64Url encoding for inline packages. It also strips dangerous shell metacharacters from imported launch arguments and reports warnings.
App.axaml.csRoute startup and IPC profile imports+87/-0
Route startup and IPC profile imports
• Handles profile import arguments and IPC messages, performs package inspection, activates the main window, and opens the pre-import inspection dialog.
GameProfileLauncherViewModel.csOpen sharing dialogs from profile cards+58/-1
Open sharing dialogs from profile cards
• Injects the sharing service into launcher profiles, generates links, loads profile details, and presents the share dialog with notification-based error handling.
GameProfileSettingsViewModel.Commands.csAdd sharing from profile settings+73/-5
Add sharing from profile settings
• Adds the share command for saved profiles and opens the generated share dialog. Existing save logic now accesses backing fields introduced for share availability notifications.
ImportProfileInspectionViewModel.csDrive pre-import review and progress+260/-0
Drive pre-import review and progress
• Maps inspection data into editable profile, manifest, installation, download, and warning state. It validates confirmation, reports acquisition progress, executes imports, and surfaces success or failure.
• Presents generated links and Discord markdown, copies either format to the clipboard, exports '.ghprofile' files through the storage picker, and reports action status.
• Adds a dark acrylic modal with editable naming, compatibility and security alerts, itemized cache/download badges, installation selection, settings controls, and an acquisition progress overlay.
1. Manifest ID deletes directories✓ Resolved🐞 Bug⛨ Security
Description
AcquireMissingManifestAsync uses the untrusted, package-controlled dependency.ManifestId to
construct stagingDir before validating it, so a rooted or traversal-containing ID can redirect
staging outside the intended temporary root. The finally block can then recursively delete the
attacker-selected writable directory after the import attempt.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The shared package supplies dependency.ManifestId as a deserialized string without path
validation, and the method uses it directly as a path component before ManifestId.TryCreate runs.
Because path construction accepts rooted components and the same method later calls recursive
deletion on the resulting stagingDir, an attacker can redirect both staging and cleanup to a
directory outside GenHub's temporary staging root.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Untrusted manifest IDs can redirect the import staging directory outside GenHub's intended temporary staging root, after which recursive cleanup may delete an attacker-selected writable directory.
## Issue Context
`ManifestId` is controlled by the shared package and is used to construct a filesystem path before `ManifestId.TryCreate` validates it. Generate staging directory names independently of package data and verify canonical containment within the intended temporary root before creating or deleting directories.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[614-621]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[673-680]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
When lookup or acquisition fails, the fallback calls AddManifestAsync for a manifest whose content
was never stored, discards the expected failure, and returns success; the normal staging branch also
ignores insertion failures. Import can consequently persist a profile containing the original
enabled dependency ID even though its required content was never acquired.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Both acquisition branches ignore the result of AddManifestAsync and report success. In particular,
the metadata-only pool overload explicitly fails when the manifest content is not already stored,
yet after the service reports acquisition success, the caller records and persists the originally
requested dependency ID in the profile.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
The import acquisition path treats failed manifest-pool insertions as successful, allowing profiles to be saved with enabled dependencies whose required content was never acquired.
## Issue Context
The fallback has no source content, while `ContentManifestPool.AddManifestAsync(manifest)` explicitly rejects a manifest when its content is not already stored. Check every `AddManifestAsync` result, return failure when insertion or storage fails, and do not synthesize successful empty dependencies or persist the requested dependency ID after failed acquisition.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[657-675]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[714-734]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[315-340]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
When no exact manifest ID appears in search results, acquisition silently selects the first result
and installs it. The resulting profile still records the originally requested ID, so it can both
download unrelated content and remain unusable.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Search uses display name/type, explicitly falls back from an exact ID match to FirstOrDefault,
while profile creation retains the package's original dependency IDs.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Fallback search installs the first nonmatching result.
## Issue Context
Acquire only an exact, validated manifest ID match and fail clearly when the requested dependency cannot be found.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[689-706]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
The acquisition path writes downloaded bytes without comparing them to the shared file's declared
SHA-256 hash or size. Changed or malicious server content is consequently accepted into CAS instead
of failing the import's integrity check.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The shared file model contains Size and Hash, but acquisition downloads and writes bytes without
reading either; downstream storage computes a new CAS hash with no expected hash rather than
validating the declared one.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Downloaded manifest files are accepted without checking declared integrity metadata.
## Issue Context
Stream each file while enforcing the declared size and SHA-256 hash, and fail the acquisition on any mismatch.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[638-653]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
5. Client game type mismatches✓ Resolved🐞 Bug≡ Correctness
Description
Import constructs a GameClient from GameClientManifestId without checking that the manifest's
TargetGame matches the shared profile's GameType. A mismatched package produces a profile bound
to a client for the wrong game despite installation compatibility being checked against the declared
game.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Installation selection uses package.Profile.GameType, but the created client's type is copied
independently from the resolved manifest and assigned without an equality check.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
A resolved client manifest may target a different game than the profile.
## Issue Context
Require the client manifest target, selected installation capability, and shared profile game type to agree before constructing the profile.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[367-384]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Malformed shared manifest IDs are replaced with random IDs during acquisition, but the original
malformed IDs are still added to EnabledContentIds. Even if storage succeeds, the imported profile
cannot reference the manifest that was actually created.
+ if (!ManifestId.TryCreate(dependency.ManifestId, out var manifestId))+ {+ manifestId = ManifestId.Create($"1.0.community.mod.{Guid.NewGuid():N}");+ }
Relevance
●●● Strong
Replacing an invalid identifier without updating dependent references is a concrete imported-data
consistency bug.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The constructed manifest receives a generated ID, whereas requiredManifestIds is populated with
the unchanged package value and later assigned to the profile.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Acquisition rewrites invalid IDs without updating profile references.
## Issue Context
Validate all dependency IDs during inspection and reject invalid packages rather than substituting random identifiers.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[619-622]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[313-320]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
7. Imports have no size bounds✓ Resolved🐞 Bug☼ Reliability
Description
Imported inline, .ghprofile, and remote Brotli payloads are fully buffered or decompressed with
GetByteArrayAsync and ReadToEndAsync without compressed-input, HTTP-response,
decompressed-output, per-file, aggregate-package, memory, or disk limits. A crafted profile can
therefore exhaust resources during automatic inspection or import, before the inspection UI or
package validation is reached.
++ /// <summary>+ /// Sanitizes command-line arguments to prevent command injection and unauthorized flags.+ /// </summary>+ /// <param name="arguments">The raw command line arguments string from the shared package.</param>+ /// <param name="warnings">Output list of warnings if potentially unsafe characters were sanitized.</param>
Relevance
●●● Strong
Resource-exhaustion safeguards for untrusted remote content align with the team's accepted hardening
of remote URL handling.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The only inline size limit is enforced during export and therefore constrains only payloads GenHub
creates; on import, the resolver accepts arbitrary URI, file, and directly encoded inputs, buffers
remote files to completion, and passes compressed bytes to a helper that reads the decompressed
stream to completion without imposing size limits.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Untrusted inline, `.ghprofile`, and remote profile payloads are buffered and decompressed without input, response, output, or package-size limits, allowing crafted packages to exhaust process memory or disk before inspection or validation completes.
## Issue Context
The inline export limit applies only to payloads created by GenHub and does not protect imports. Add limits for encoded and decompressed package sizes, stream HTTP responses instead of buffering them completely, enforce per-file and aggregate limits across URI, file, and directly encoded inputs, and abort processing before any limit is exceeded.
## Fix Focus Areas
- GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs[74-86]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[532-603]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[652-653]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
The importer combines the untrusted file.RelativePath with its staging directory without
canonicalizing the destination or checking containment. A shared manifest can use .. segments or a
rooted path to write downloaded bytes outside staging and overwrite arbitrary files writable with
GenHub's permissions.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
ManifestFile.RelativePath comes from the shared package and is combined directly with the staging
root before parent-directory creation and file writing; the same package-controlled paths are
subsequently passed to content storage, which also resolves them from the source directory using
Path.Combine.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
A shared package can supply an absolute or traversal `RelativePath`, causing the importer to escape its staging directory and write downloaded bytes to arbitrary locations writable by GenHub.
## Issue Context
The downloader creates parent directories and writes the remote response to the combined path without validating that it remains under the staging root. Reject rooted paths and ensure the canonical destination remains beneath the canonical staging root before creating directories or writing bytes; account for the subsequent content-storage resolution of the same paths.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[638-653]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[660-668]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Inspection and acquisition pass attacker-controlled profile and file URLs directly to HttpClient.
Opening a deep link can therefore make GenHub request loopback, private-network, or other unintended
endpoints before the user confirms import.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Shared profile URLs can trigger unrestricted outbound requests.
## Issue Context
Allow only intended HTTPS sources, reject local/private/link-local destinations after DNS resolution, and constrain redirects; apply the same policy to profile and manifest-file URLs.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[550-561]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[638-653]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
CancelCommand closes the modal while import always runs with CancellationToken.None. The
operation can continue downloading and create a profile after the user has cancelled, with
subsequent progress or errors hidden.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The confirm path explicitly passes CancellationToken.None, while the cancel path immediately
raises the close event and the window closes on that event.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Closing the dialog does not cancel an active import.
## Issue Context
Own a cancellation token source for the import, cancel and await it on user cancellation/window close, or disable closing until completion.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[171-172]
- GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[209-209]
- GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[233-237]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
The inspection view model exposes untrusted IconPath and CoverPath directly to image bindings.
The repository's image converter opens any existing rooted path, so merely inspecting a package can
read an attacker-selected local or UNC image path.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Shared artwork paths can access arbitrary rooted filesystem locations.
## Issue Context
Do not bind package-provided paths directly; permit only packaged assets or explicitly trusted application assets and reject rooted/UNC paths.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[137-145]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[399-400]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
12. Import bypasses name limit✓ Resolved🐞 Bug≡ Correctness
Description
ImportSharedProfileAsync validates only that the name is nonblank, despite defining a
100-character sharing limit and normal profile creation rejecting longer names. Because import saves
directly through the repository, overlong package or user-edited names bypass the application's
profile-name invariant.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Sharing declares a 100-character maximum and the normal manager rejects names over 100, but import
checks only whitespace and then calls the repository directly.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Shared imports bypass the normal 100-character profile-name validation.
## Issue Context
Apply the same centralized name validation used by profile creation before downloads or repository persistence.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[301-304]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[389-412]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
View medium (4) 13. Some shared settings disappear✓ Resolved🐞 Bug≡ Correctness
Description
VideoTextureQuality and TshSystemTimeFontSize are serialized into GameSettingsOverrides, but
ApplySettingsOverridesToProfile never applies either key. As a result, profiles imported with game
settings enabled silently lose the sender's texture-quality and system-time-font-size overrides,
breaking settings round trips.
+ if (overrides.TryGetValue(nameof(profile.AudioSpeechVolume), out var speechObj) && speechObj is JsonElement speechElem && speechElem.TryGetInt32(out var speech)) profile.AudioSpeechVolume = speech;+ if (overrides.TryGetValue(nameof(profile.EnableVideoShadows), out var shadowsObj) && shadowsObj is JsonElement shadowsElem) profile.EnableVideoShadows = shadowsElem.GetBoolean();++ // TSH settings+ if (overrides.TryGetValue(nameof(profile.TshArchiveReplays), out var tshReplayObj) && tshReplayObj is JsonElement tshReplayElem) profile.TshArchiveReplays = tshReplayElem.GetBoolean();+ if (overrides.TryGetValue(nameof(profile.TshRenderFpsFontSize), out var tshFpsObj) && tshFpsObj is JsonElement tshFpsElem && tshFpsElem.TryGetInt32(out var fpsSize)) profile.TshRenderFpsFontSize = fpsSize;+ if (overrides.TryGetValue(nameof(profile.TshNetworkLatencyFontSize), out var tshLatObj) && tshLatObj is JsonElement tshLatElem && tshLatElem.TryGetInt32(out var latSize)) profile.TshNetworkLatencyFontSize = latSize;++ // GO settings+ if (overrides.TryGetValue(nameof(profile.GoShowFps), out var goFpsObj) && goFpsObj is JsonElement goFpsElem) profile.GoShowFps = goFpsElem.GetBoolean();+ if (overrides.TryGetValue(nameof(profile.GoShowPing), out var goPingObj) && goPingObj is JsonElement goPingElem) profile.GoShowPing = goPingElem.GetBoolean();+ if (overrides.TryGetValue(nameof(profile.GoShowPlayerRanks), out var goRanksObj) && goRanksObj is JsonElement goRanksElem) profile.GoShowPlayerRanks = goRanksElem.GetBoolean();+ if (overrides.TryGetValue(nameof(profile.GoRenderFpsLimit), out var goFpsLimitObj) && goFpsLimitObj is JsonElement goFpsLimitElem && goFpsLimitElem.TryGetInt32(out var fpsLimit)) profile.GoRenderFpsLimit = fpsLimit;
Relevance
●●● Strong
Deterministic missing-mapping bug breaking settings round trip; team accepts such correctness fixes.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The export mapping emits both VideoTextureQuality and TshSystemTimeFontSize, while the complete
apply method has no branch for either corresponding GameProfile property: its TSH handling ends
after network-latency font size, and it contains no texture-quality mapping.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Two settings included in the exported override dictionary, `VideoTextureQuality` and `TshSystemTimeFontSize`, are omitted from import application, so profile sharing does not provide a complete settings round trip.
## Issue Context
`JsonSerializer` deserializes override values to `JsonElement`. Add type-safe deserialization and application for the texture-quality enum and the missing TSH system-time font size using the same guarded conversions as neighboring settings, and test round trips for every exported key.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[747-764]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[771-790]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
14. Schema version is ignored✓ Resolved🐞 Bug≡ Correctness
Description
Inspection accepts every SchemaVersion and import consumes the package with version-1 semantics.
Incompatible packages are therefore not rejected and can be silently interpreted incorrectly.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Package schema versions are declared but never validated.
## Issue Context
Validate supported versions immediately after deserialization and before inspection/import; add migration handling only for explicitly supported versions.
## Fix Focus Areas
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[168-182]
- GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[306-310]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
15. Import dialogs can overlap✓ Resolved🐞 Bug☼ Reliability
Description
Every IPC import starts an independent fire-and-forget inspection and modal operation with no
serialization or existing-dialog guard. Closely spaced profile links can therefore open concurrent
modal children for the same main window and race imports.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Concurrent IPC commands can create overlapping modal import flows.
## Issue Context
Queue or coalesce import requests and permit only one owned inspection/import dialog at a time.
## Fix Focus Areas
- GenHub/GenHub/App.axaml.cs[281-287]
- GenHub/GenHub/App.axaml.cs[308-343]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
16. Share payloads leak to logs✓ Resolved🐞 Bug⛨ Security
Description
The startup and IPC paths log complete profile URIs and local package paths at information level.
Inline URIs contain the full encoded profile package, so logs retain shared configuration data and
potentially sensitive local path information.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Complete profile payloads and local paths are written to logs.
## Issue Context
Log only the import source type or a nonreversible correlation hash; redact query values and filesystem details at every startup/IPC log site.
## Fix Focus Areas
- GenHub/GenHub/App.axaml.cs[239-240]
- GenHub/GenHub/App.axaml.cs[283-285]
- GenHub/GenHub/App.axaml.cs[310-310]
- GenHub/GenHub.Windows/Program.cs[88-91]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Context sources
Review mode: 🧠 Deep: This is a dense, security-sensitive cross-cutting feature spanning protocol/IPC startup routing, compression and sanitization, import/install logic, persistence-facing services, and substantial UI paths, creating many independent opportunities for subtle defects.
Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR
The reason will be displayed to describe this comment to others. Learn more.
WARNING: Migration failure is silently swallowed here — migrateResult.FirstError is never logged when StoreContentAsync fails, and the user then sees the misleading "Cannot create hard link across different volumes" warning at line 133 instead of the real root cause.
Before this increment the code logged migrateResult.FirstError and returned false immediately. The new control flow only logs success and falls through to the cross-volume branch, which makes CAS pool migration failures indistinguishable from genuine cross-volume layouts.
The reason will be displayed to describe this comment to others. Learn more.
SUGGESTION:IsCustomLocalManifest returns false for every ContentType.GameClient manifest, regardless of PublisherType. Any future game-client publisher (besides GeneralsOnline) that legitimately carries local-only mods under .gameclient. will be reported as a non-local manifest, and the dependency will silently lack a PackageUrl/packageHash while still being marked as not requiring cloud upload.
The previous gameinstallation-segment guard was sufficient. Consider narrowing the early return to manifests whose PublisherType is one of the well-known provider constants (e.g. GeneralsOnline), or to the specific .gameinstallation. / .gameclient.<provider>. ID shapes, so that genuinely local GameClient mods (such as a packaged 1.0.local.gameclient.somemod) are still classified as local.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
The reason will be displayed to describe this comment to others. Learn more.
WARNING:RegisterExtractedManifestAsync previously consulted publisherManifestFactoryResolver.ResolveFactory(contentManifest) and forwarded any factory-created manifests into manifestPool.AddManifestAsync. The new code only adds the single re-built contentManifest, dropping the factory-derived manifests entirely.
For extracted cloud packages whose publisher exposes a factory (e.g. ModDB / CnCLabs / AoDMaps factories), the import path will now register only the parent manifest and skip the additional manifests those factories emit, so subsequent reconciliation will not see them and the workspace build will be incomplete. This is a behavior regression introduced in this increment.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
The reason will be displayed to describe this comment to others. Learn more.
WARNING: The new copy fallback (FileOperations.CopyFileAsync) here and at lines 270, 387, and 410 silently swallows any exception thrown by the copy operation. CopyFileAsync returning false would be the only signal, but its callers in the same catch block do not check the result before continuing.
In ProcessLocalFileAsync and ProcessStandardFileAsync, the surrounding catch block already logs and proceeds; if CopyFileAsync itself throws (disk full, permission denied, antivirus interception), the exception unwinds to the outer workspace PrepareAsync and the user sees an opaque Workspace preparation failed toast with no information about which file fell back. Either await the task and let exceptions propagate, or wrap it in its own try/catch that logs the file + exception context.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
The reason will be displayed to describe this comment to others. Learn more.
SUGGESTION: The dependency.Files.Select(... new ManifestFile { ... }) block at lines 1244-1238 (in BuildManifestDependencyAsync), 1252-1266 (uploaded-URL variant), and 1744-1758 (in RegisterExtractedManifestAsync) is duplicated three times. Per AGENTS.md constants/code-style guidance against magic duplication, extract a single private helper such as ToSharedManifestFile(ManifestFile source) returning a normalized ManifestFile and call it from all three sites so adding a new ManifestFile property only has to be wired once.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
The reason will be displayed to describe this comment to others. Learn more.
Lambda's body can be simplified
If your lambda's body has a single statement, consider refactoring it to move away from block syntax to expression body. Doing so makes your code easier to read.
Fix orphaned ToolTip element causing InvalidCastException in GameProfileSettingsContentView when opening the Info tab. Fix unrecognized cursor type 'Default' in SetupWizardView by replacing it with Avalonia's standard 'Arrow' cursor.
Model: Gemini 3.7 Flash
Harness: Antigravity CLI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements GitHub Issue #390: GameProfile sharing via
genhub://profile/importprotocol,.ghprofilefile packaging, and a rich dark acrylic pre-import inspection modal with manifest cache diffing and safety validation.Motivation
Players and community creators need a frictionless, secure way to share curated game profiles (such as Generals Online competitive setups, Shockwave mods, map packs, resolution and audio configurations) directly via deep links (e.g. Discord, browsers) or exported files without manually setting up dependencies.
Changes
SharedGameProfilePackage,SharedProfileMetadata,SharedManifestDependency,SharedProfileInspectionResult, andSharedProfileImportRequestinGenHub.Core.Models.GameProfile.genhub://profile/import,--import-profile,.ghprofile) inCommandLineConstantsandProfileSharingConstants.ProfileSharingCompressionHelperwith Brotli compression, Base64Url codec, and shell injection token sanitization (|,&,;,>,<,`,$).IProfileSharingService/ProfileSharingServicehandling URI generation,.ghprofileexport/import, pre-import manifest diffing against local CAS cache, installation matching, name conflict resolution, and publisher factory resolution viaPublisherManifestFactoryResolver.IProfileSharingServiceinGameProfileModule.ShareProfileDialogWindow.axaml&ShareProfileDialogViewModel(dark acrylic glass dialog for copying protocol URIs, copying Discord invite markdown, and exporting.ghprofilefiles).ImportProfileInspectionWindow.axaml&ImportProfileInspectionViewModel(rich inspection modal showing icon/cover, itemized manifest breakdown with[✅ CACHED]vs[⬇️ Size]badges, installation selector, settings toggles, security alerts, and live progress overlay).GameProfileSettingsWindowheader bar andGameProfileCardViewcontext menu.--import-profile,genhub://profile/...) and single-instance IPC forwarding (import-profile:) inApp.axaml.csandGenHub.Windows/Program.cs.GenHub.Tests.CoreforProfileSharingService,ProfileSharingCompressionHelper,CommandLineParser,ShareProfileDialogViewModel, andImportProfileInspectionViewModel.Verification
GenHub.Tests.Coreexecuted and passing