Skip to content

feat(profiles): implement profile sharing via genhub:// protocol and rich inspection UI - #400

Open
undead2146 wants to merge 70 commits into
developmentfrom
feat/gameprofile-sharing-and-inspection
Open

feat(profiles): implement profile sharing via genhub:// protocol and rich inspection UI#400
undead2146 wants to merge 70 commits into
developmentfrom
feat/gameprofile-sharing-and-inspection

Conversation

@undead2146

Copy link
Copy Markdown
Member

Summary

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
  • Solution builds cleanly with 0 errors
  • Protocol routing, Brotli codec, argument sanitization, and UI models thoroughly verified

Comment thread GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.cs Outdated
@undead2146 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
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add secure game profile sharing and import inspection

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

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.

GenHub/GenHub.Core/Constants/CommandLineConstants.cs

IpcCommands.csAdd profile import IPC command prefix +6/-0

Add profile import IPC command prefix

• Defines the command used to forward profile import targets from secondary processes to the primary application instance.

GenHub/GenHub.Core/Constants/IpcCommands.cs

ProfileSharingConstants.csCentralize profile package and sharing limits +48/-0

Centralize profile package and sharing limits

• Defines schema, inline payload, file extension, Discord template, conflict suffix, and profile-name constants for the sharing workflow.

GenHub/GenHub.Core/Constants/ProfileSharingConstants.cs

CommandLineParser.csParse profile links and package paths +37/-0

Parse profile links and package paths

• Recognizes direct profile protocol URIs, spaced and inline import flags, and '.ghprofile' file arguments.

GenHub/GenHub.Core/Helpers/CommandLineParser.cs

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.

GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs

IProfileSharingService.csDefine the profile sharing service contract +67/-0

Define the profile sharing service contract

• Introduces APIs for URI, JSON, and file exports, non-mutating inspection, confirmed imports, and Discord markdown generation.

GenHub/GenHub.Core/Interfaces/GameProfiles/IProfileSharingService.cs

SharedGameProfilePackage.csModel the versioned shared profile container +36/-0

Model the versioned shared profile container

• Defines package schema metadata, exported profile settings, and required manifest dependencies.

GenHub/GenHub.Core/Models/GameProfile/SharedGameProfilePackage.cs

SharedManifestDependency.csModel portable manifest dependencies +66/-0

Model portable manifest dependencies

• Captures manifest identity, publisher metadata, content type, size, cache state, hash, and downloadable files for inspection and acquisition.

GenHub/GenHub.Core/Models/GameProfile/SharedManifestDependency.cs

SharedProfileImportRequest.csModel confirmed profile import options +34/-0

Model confirmed profile import options

• Carries the inspected package, chosen name and installation, workspace strategy, and settings inclusion choice into import execution.

GenHub/GenHub.Core/Models/GameProfile/SharedProfileImportRequest.cs

SharedProfileInspectionResult.csModel pre-import compatibility results +70/-0

Model pre-import compatibility results

• Aggregates cache differences, compatible installations, download totals, naming conflicts, security warnings, and the source package.

GenHub/GenHub.Core/Models/GameProfile/SharedProfileInspectionResult.cs

SharedProfileMetadata.csModel shared profile configuration metadata +70/-0

Model shared profile configuration metadata

• Captures presentation, game client, workspace, launch, Steam, and game-setting overrides needed to recreate a profile.

GenHub/GenHub.Core/Models/GameProfile/SharedProfileMetadata.cs

Program.csForward profile imports to the primary instance +10/-0

Forward profile imports to the primary instance

• Extracts profile-sharing startup arguments and sends them through single-instance IPC before focusing the existing application window.

GenHub/GenHub.Windows/Program.cs

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.

GenHub/GenHub/App.axaml.cs

ProfileSharingService.csImplement end-to-end profile package sharing +792/-0

Implement end-to-end profile package sharing

• Builds and serializes packages, resolves URI/file/remote payloads, inspects cache and installation compatibility, sanitizes arguments, resolves naming conflicts, acquires missing content, and persists imported profiles. It also supports publisher-specific manifest factories and optional game-setting restoration.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs

GameProfileItemViewModel.csExpose profile card sharing command +17/-0

Expose profile card sharing command

• Adds an injectable share action and relay command to profile card items.

GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileItemViewModel.cs

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.

GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileLauncherViewModel.cs

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.

GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.Commands.cs

GameProfileSettingsViewModel.Initialization.csTrack share availability during initialization +5/-3

Track share availability during initialization

• Updates profile ID and workspace backing fields directly and notifies the UI when the current profile becomes shareable.

GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.Initialization.cs

GameProfileSettingsViewModel.csInject sharing into profile settings +11/-4

Inject sharing into profile settings

• Adds the optional sharing service, explicit backing state for the current profile, and a 'CanShareProfile' property for saved-profile visibility.

GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.cs

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.

GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs

ShareProfileDialogViewModel.csDrive profile sharing actions +188/-0

Drive profile sharing actions

• Presents generated links and Discord markdown, copies either format to the clipboard, exports '.ghprofile' files through the storage picker, and reports action status.

GenHub/GenHub/Features/GameProfiles/ViewModels/ShareProfileDialogViewModel.cs

SharedInstallationOption.csRepresent import installation choices +22/-0

Represent import installation choices

• Adds a display model for compatible installation identifiers, names, and paths used by the import selector.

GenHub/GenHub/Features/GameProfiles/ViewModels/SharedInstallationOption.cs

GameProfileCardView.axamlAdd sharing to the profile card menu +6/-0

Add sharing to the profile card menu

• Adds a Share Profile context-menu command and icon to each profile card.

GenHub/GenHub/Features/GameProfiles/Views/GameProfileCardView.axaml

GameProfileSettingsWindow.axamlAdd profile settings share action +10/-0

Add profile settings share action

• Adds a header share button that is visible only after the profile has been saved.

GenHub/GenHub/Features/GameProfiles/Views/GameProfileSettingsWindow.axaml

ImportProfileInspectionWindow.axamlCreate rich profile import inspection window +422/-0

Create rich profile import inspection window

• 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.

GenHub/GenHub/Features/GameProfiles/Views/ImportProfileInspectionWindow.axaml

ImportProfileInspectionWindow.axaml.csWire import inspector window closure +34/-0

Wire import inspector window closure

• Loads the Avalonia view and closes the modal when its view model raises a close request.

GenHub/GenHub/Features/GameProfiles/Views/ImportProfileInspectionWindow.axaml.cs

ShareProfileDialogWindow.axamlCreate acrylic profile sharing dialog +221/-0

Create acrylic profile sharing dialog

• Adds a themed modal for copying protocol links, copying Discord invites, exporting package files, and displaying action feedback.

GenHub/GenHub/Features/GameProfiles/Views/ShareProfileDialogWindow.axaml

ShareProfileDialogWindow.axaml.csWire share dialog window closure +34/-0

Wire share dialog window closure

• Loads the Avalonia sharing view and responds to its view model's close event.

GenHub/GenHub/Features/GameProfiles/Views/ShareProfileDialogWindow.axaml.cs

Tests (5) +865 / -0
ProfileSharingServiceTests.csTest profile export, inspection, and import +350/-0

Test profile export, inspection, and import

• Covers protocol URI generation, missing-profile failures, file export, manifest cache inspection, installation matching, conflict resolution, security warnings, persistence, and Discord formatting.

GenHub/GenHub.Tests/GenHub.Tests.Core/Features/GameProfiles/ProfileSharingServiceTests.cs

ImportProfileInspectionViewModelTests.csTest import inspection presentation and commands +216/-0

Test import inspection presentation and commands

• Verifies inspection-result mapping, compatible installation selection, import request creation, successful closure, and failed-import error presentation.

GenHub/GenHub.Tests/GenHub.Tests.Core/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModelTests.cs

ShareProfileDialogViewModelTests.csTest share dialog state and closure +83/-0

Test share dialog state and closure

• Verifies profile presentation, generated sharing content, theme values, and the close-request command.

GenHub/GenHub.Tests/GenHub.Tests.Core/Features/GameProfiles/ViewModels/ShareProfileDialogViewModelTests.cs

CommandLineParserSharingTests.csTest profile-sharing argument parsing +95/-0

Test profile-sharing argument parsing

• Covers direct protocol URIs, both import flag forms, package file paths, and unrelated argument sets.

GenHub/GenHub.Tests/GenHub.Tests.Core/Helpers/CommandLineParserSharingTests.cs

ProfileSharingCompressionHelperTests.csTest payload encoding and launch sanitization +121/-0

Test payload encoding and launch sanitization

• Covers synchronous and asynchronous codec round trips, invalid payload handling, safe arguments, empty values, and dangerous shell-character removal.

GenHub/GenHub.Tests/GenHub.Tests.Core/Helpers/ProfileSharingCompressionHelperTests.cs

Other (1) +3 / -0
GameProfileModule.csRegister the profile sharing service +3/-0

Register the profile sharing service

• Adds the scoped 'IProfileSharingService' implementation to the game-profile dependency injection module.

GenHub/GenHub/Infrastructure/DependencyInjection/GameProfileModule.cs

@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Aug 19, 2026
@qodo-code-review

qodo-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R614-615]

+                var stagingDir = Path.Combine(Path.GetTempPath(), "GenHub", "SharedImportStaging", dependency.ManifestId);
+                Directory.CreateDirectory(stagingDir);
Relevance

●●● Strong

Untrusted path components controlling filesystem staging and recursive cleanup are a concrete
security boundary violation.

PR-#195

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[611-615]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[673-680]
GenHub/GenHub.Core/Models/GameProfile/SharedManifestDependency.cs[12-15]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[612-621]

Agent prompt
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


2. Failed dependencies report success ✓ Resolved 🐞 Bug ≡ Correctness
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R720-734]

+            var fallbackManifest = new ContentManifest
+            {
+                Id = fallbackId,
+                Name = dependency.DisplayName,
+                Version = dependency.Version,
+                ContentType = dependency.ContentType,
+                Publisher = new PublisherInfo
+                {
+                    Name = dependency.Publisher ?? "Community",
+                    PublisherType = dependency.PublisherType ?? PublisherTypeConstants.Unknown,
+                },
+            };
+
+            await manifestPool.AddManifestAsync(fallbackManifest, cancellationToken);
+            return OperationResult<bool>.CreateSuccess(true);
Relevance

●●● Strong

Accepted precedent favors surfacing operation failures instead of silently reporting success.

PR-#137

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[315-340]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[714-734]
GenHub/GenHub/Features/Manifest/ContentManifestPool.cs[39-55]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[389-404]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[657-671]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[720-734]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[313-320]

Agent prompt
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


3. Search installs unrelated content ✓ Resolved 🐞 Bug ≡ Correctness
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R701-706]

+                var match = searchResult.Data.FirstOrDefault(r => r.Id.Equals(dependency.ManifestId, StringComparison.OrdinalIgnoreCase))
+                    ?? searchResult.Data.FirstOrDefault();
+
+                if (match != null)
+                {
+                    var acquireRes = await contentOrchestrator.AcquireContentAsync(match, progress, cancellationToken);
Relevance

●●● Strong

Similar to accepted false-positive match fix; silently installing wrong content is a real
correctness bug.

PR-#199

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[689-706]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[313-320]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[389-398]

Agent prompt
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


View high (6)
4. Downloaded content lacks verification ✓ Resolved 🐞 Bug ⛨ Security
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R652-653]

+                        var bytes = await httpClient.GetByteArrayAsync(file.DownloadUrl, cancellationToken);
+                        await File.WriteAllBytesAsync(destination, bytes, cancellationToken);
Relevance

●●● Strong

Accepted security-hardening precedents support validating untrusted remote content before trusting
it.

PR-#195

ⓘ 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.

GenHub/GenHub.Core/Models/Manifest/ManifestFile.cs[30-40]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[638-653]
GenHub/GenHub/Features/Content/Services/ContentStorageService.cs[764-800]

Agent prompt
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R374-378]

+                        Id = clientManifestResult.Data.Id.ToString(),
+                        Name = clientManifestResult.Data.Name,
+                        Version = clientManifestResult.Data.Version,
+                        GameType = clientManifestResult.Data.TargetGame,
+                        InstallationId = targetInstallation.Id,
Relevance

●●● Strong

Independent game-type values create a concrete correctness mismatch, matching accepted semantic
validation hardening.

PR-#181
PR-#195

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[354-362]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[367-396]

Agent prompt
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


6. Generated manifest IDs disconnect ✓ Resolved 🐞 Bug ≡ Correctness
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R619-622]

+                    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.

PR-#199
PR-#181

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[313-320]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[619-635]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[389-398]

Agent prompt
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.
Code

GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs[R81-86]

+
+    /// <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.

PR-#195
PR-#204

ⓘ 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.

GenHub/GenHub.Core/Constants/ProfileSharingConstants.cs[13-17]
GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs[74-86]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[652-653]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[532-603]

Agent prompt
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


8. Downloaded files escape staging ✓ Resolved 🐞 Bug ⛨ Security
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R645-653]

+                        var destination = Path.Combine(stagingDir, file.RelativePath);
+                        var destinationDir = Path.GetDirectoryName(destination);
+                        if (!string.IsNullOrEmpty(destinationDir))
+                        {
+                            Directory.CreateDirectory(destinationDir);
+                        }
+
+                        var bytes = await httpClient.GetByteArrayAsync(file.DownloadUrl, cancellationToken);
+                        await File.WriteAllBytesAsync(destination, bytes, cancellationToken);
Relevance

●●● Strong

Untrusted relative paths escaping a staging root are a concrete filesystem security flaw, consistent
with accepted hardening patterns.

PR-#195

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[638-653]
GenHub/GenHub/Features/Content/Services/ContentStorageService.cs[701-710]
GenHub/GenHub.Core/Models/Manifest/ManifestFile.cs[9-15]

Agent prompt
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


9. Shared links enable SSRF ✓ Resolved 🐞 Bug ⛨ Security
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R560-561]

+                    var response = await httpClient.GetStringAsync(url, cancellationToken);
+                    return OperationResult<string>.CreateSuccess(response);
Relevance

●●● Strong

Direct attacker-controlled URL fetching is closely aligned with the team's accepted URL-host
validation hardening.

PR-#195

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both the url= query value and each shared DownloadUrl are fetched directly, with no scheme,
host, address, or redirect validation in between.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[550-561]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[638-653]
GenHub/GenHub.Core/Models/Manifest/ManifestFile.cs[53-56]

Agent prompt
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



Remediation recommended

10. Cancel leaves import running ✓ Resolved 🐞 Bug ☼ Reliability
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[R233-237]

+    [RelayCommand]
+    private void Cancel()
+    {
+        CloseRequested?.Invoke(this, EventArgs.Empty);
+    }
Relevance

●●● Strong

Reliability gap around cancellation/failure propagation matches accepted pattern of surfacing
failures.

PR-#137

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[188-216]
GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[233-237]
GenHub/GenHub/Features/GameProfiles/Views/ImportProfileInspectionWindow.axaml.cs[27-31]

Agent prompt
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


11. Artwork opens arbitrary paths ✓ Resolved 🐞 Bug ⛨ Security
Description
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.
Code

GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[R137-138]

+        CoverPath = inspectionResult.ProfileMetadata.CoverPath;
+        IconPath = inspectionResult.ProfileMetadata.IconPath;
Relevance

●●● Strong

Accepted security precedent supports validating untrusted input before it reaches sensitive
operations.

PR-#195

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The view model copies shared paths unchanged, and StringToImageConverter opens rooted existing
paths with new Bitmap(path).

GenHub/GenHub/Features/GameProfiles/ViewModels/ImportProfileInspectionViewModel.cs[137-145]
GenHub/GenHub/Infrastructure/Converters/StringToImageConverter.cs[52-64]

Agent prompt
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R301-304]

+            if (string.IsNullOrWhiteSpace(request.ProfileName))
+            {
+                return OperationResult<GameProfile>.CreateFailure("Profile name cannot be empty.");
+            }
Relevance

●●● Strong

Import bypassing an existing invariant is a concrete validation gap, consistent with accepted
input-validation hardening.

PR-#181
PR-#165

ⓘ 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.

GenHub/GenHub.Core/Constants/ProfileSharingConstants.cs[44-47]
GenHub/GenHub/Features/GameProfiles/Services/GameProfileManager.cs[325-341]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[301-304]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[389-412]

Agent prompt
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R778-790]

+        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.

PR-#199

ⓘ 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.

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[747-760]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[771-790]
GenHub/GenHub.Core/Models/GameProfile/GameProfile.cs[109-110]
GenHub/GenHub.Core/Models/GameProfile/GameProfile.cs[256-256]

Agent prompt
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.
Code

GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[R179-182]

+            if (package == null || package.Profile == null)
+            {
+                return OperationResult<SharedProfileInspectionResult>.CreateFailure("Package does not contain valid profile metadata.");
+            }
Relevance

●●● Strong

Rejecting unsupported input variants matches accepted validation-hardening findings for content and
identifiers.

PR-#181
PR-#165

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The model exposes a schema version with a defined current value, but the only post-deserialization
validation checks package/profile nullability.

GenHub/GenHub.Core/Models/GameProfile/SharedGameProfilePackage.cs[12-15]
GenHub/GenHub.Core/Constants/ProfileSharingConstants.cs[8-11]
GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs[168-182]

Agent prompt
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.
Code

GenHub/GenHub/App.axaml.cs[R281-287]

+        else if (command.StartsWith(IpcCommands.ImportProfilePrefix, StringComparison.OrdinalIgnoreCase))
+        {
+            var shareUri = command[IpcCommands.ImportProfilePrefix.Length..];
+            logger?.LogInformation("Received IPC profile import command: {ShareUri}", shareUri);
+
+            // Handle profile import
+            SafeFireAndForget(HandleImportProfileUriAsync(shareUri, mainWindow), nameof(HandleImportProfileUriAsync));
Relevance

●●● Strong

Concurrency and fire-and-forget reliability issues align with the team's accepted async-operation
corrections.

PR-#204

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The IPC branch starts each import task without awaiting or guarding it, and each task later invokes
ShowDialog(mainWindow).

GenHub/GenHub/App.axaml.cs[281-287]
GenHub/GenHub/App.axaml.cs[308-343]

Agent prompt
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.
Code

GenHub/GenHub/App.axaml.cs[R239-240]

+        var logger = _serviceProvider.GetService<ILogger<App>>();
+        logger?.LogInformation("Startup profile import detected: {ShareUri}", shareUri);
Relevance

●● Moderate

Sensitive logging concerns are plausible, but history lacks a close accepted precedent for redacting
application-level share payloads.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The parser accepts inline URIs and .ghprofile paths, and each new startup/IPC path logs the
extracted value verbatim.

GenHub/GenHub.Core/Helpers/CommandLineParser.cs[92-116]
GenHub/GenHub/App.axaml.cs[233-240]
GenHub/GenHub/App.axaml.cs[281-310]
GenHub/GenHub.Windows/Program.cs[87-91]

Agent prompt
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


Grey Divider

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.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/App.axaml.cs
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 19, 2026
Comment thread GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs Outdated
Comment thread GenHub/GenHub.Core/Helpers/ProfileSharingCompressionHelper.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
@undead2146
undead2146 force-pushed the feat/gameprofile-sharing-and-inspection branch from 70b2610 to 21d84da Compare August 19, 2026 14:49
Comment thread GenHub/GenHub/Common/Views/MainWindow.axaml.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
@undead2146
undead2146 force-pushed the feat/gameprofile-sharing-and-inspection branch 2 times, most recently from bc47758 to ef28863 Compare August 19, 2026 14:58
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Aug 19, 2026
@undead2146
undead2146 force-pushed the feat/gameprofile-sharing-and-inspection branch from ef28863 to e8d5242 Compare August 19, 2026 15:05
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 19, 2026
@undead2146
undead2146 force-pushed the feat/gameprofile-sharing-and-inspection branch from e8d5242 to 60a39cd Compare August 19, 2026 15:09
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 19, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Aug 19, 2026
@undead2146
undead2146 force-pushed the feat/gameprofile-sharing-and-inspection branch from 60a39cd to 393a16a Compare August 20, 2026 16:51
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 20, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 20, 2026
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Views/ImportProfileInspectionWindow.axaml Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Services/ProfileSharingService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/Views/ImportProfileInspectionWindow.axaml Outdated
undead2146 added a commit that referenced this pull request Aug 30, 2026
// Store the content in the correct pool (determined by contentType)
var migrateResult = await casService.StoreContentAsync(casSourcePath, contentType.Value, hash, cancellationToken).ConfigureAwait(false);
if (!migrateResult.Success)
if (migrateResult.Success)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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.

Suggested change
if (migrateResult.Success)
var migrateResult = await casService.StoreContentAsync(casSourcePath, contentType.Value, hash, cancellationToken).ConfigureAwait(false);
if (!migrateResult.Success)
{
logger.LogError("Failed to migrate content {Hash} to correct CAS pool: {Error}", hash, migrateResult.FirstError);
return false;
}

Reply with @kilocode-bot fix it to have Kilo Code address this issue.


private static bool IsCustomLocalManifest(ContentManifest manifest)
{
if (manifest.ContentType == ContentType.GameInstallation ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

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.

}).ToList(),
};

var addResult = await manifestPool.AddManifestAsync(contentManifest, stagingDir, cancellationToken: cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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.

file.RelativePath);

throw WrapLinkException(file.RelativePath, symlinkEx);
await FileOperations.CopyFileAsync(sourcePath, targetPath, cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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.

Name = dependency.Publisher ?? ProfileSharingConstants.DefaultLocalPublisherName,
PublisherType = dependency.PublisherType ?? PublisherTypeConstants.Local,
},
Files = dependency.Files.Select(f => new ManifestFile

Copy link
Copy Markdown

Choose a reason for hiding this comment

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.

Comment on lines +1503 to +1506
.Callback<long, string, string, string?, string?, string?, string?>((size, url, name, key, token, hash, cat) =>
{
savedHash = hash;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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
@sonarqubecloud

Copy link
Copy Markdown

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