Skip to content

Add an execution engine for manifest-declared Installation Instructions (PreInstallSteps/PostInstallSteps) #342

Description

@undead2146

Problem

ContentManifest.InstallationInstructions (PreInstallSteps / PostInstallSteps) exists as a per-manifest, publisher-agnostic hook for installation-time actions, but nothing in the codebase executes it. ContentManifestBuilder.AddPreInstallStep / AddPostInstallStep are the only writers; there is no reader anywhere in the pipeline.

It is also not reliably carried through delivery today:

  • CommunityOutpostDeliverer, GitHubManifestFactory, SuperHackersManifestFactory, and CommunityOutpostManifestFactory forward the full InstallationInstructions object.
  • HttpContentDeliverer and FileSystemDeliverer only forward WorkspaceStrategy via WithInstallationInstructions(workspaceStrategy), silently dropping any Pre/PostInstallSteps set upstream.

Raised in #336: GeneralsOnline's 060526_QFE1 portable needs a one-time step after extraction (run EasyAntiCheat_EOS_Setup.exe if not already installed) before the content is usable. That need is generic — any publisher may eventually need to run an installer, remove a stale file, or rename an extracted artifact once, at acquisition time — not GeneralsOnline-specific.

Why a generic executor needs care, not just wiring

InstallationStep today is Name + free-form Command + free-form Arguments + WorkingDirectory + RequiresElevation. Executing that as-is means any deliverer — including GitHub-sourced releases and other third-party providers — can declare an elevated, arbitrary command in manifest data. That is a real trust-boundary gap, not a hypothetical one, and it should not be closed by simply adding a generic foreach (var step in instructions.PostInstallSteps) Process.Start(...).

Proposed approach

  • Replace (or extend) InstallationStep's free-form Command/Arguments with a closed, typed Kind (e.g. RunVerifiedInstaller, RemoveFile, RenameFile), each with its own structured parameters, so the executor is auditable rather than "run anything."
  • Restrict any file a step references (installer, target file) to a path that resolves inside the manifest's own Files list (content-addressable, hash-verified) and inside the delivered/workspace directory — the same containment check GameLauncher already applies to the main executable path.
  • Fix HttpContentDeliverer / FileSystemDeliverer to forward Pre/PostInstallSteps, not just WorkspaceStrategy.
  • Add an InstallationInstructionsService (or similar) invoked once per content acquisition, after a deliverer finishes producing files, that enumerates and executes a manifest's PostInstallSteps.
  • Decide, and gate behind an allowlist if needed, which publishers are trusted to declare executable steps at all until this has had a security pass — see the Security & Integrity Validation milestone.

Acceptance criteria

  • A manifest's PostInstallSteps survive every delivery path unchanged, including HttpContentDeliverer and FileSystemDeliverer.
  • A generic service executes typed, closed-kind steps only; there is no path from manifest data to an arbitrary shell command.
  • GeneralsOnlineManifestFactory can declare "run EasyAntiCheat_EOS_Setup.exe if not installed" as a PostInstallStep and have it actually execute once, at acquisition time.

Explicitly out of scope

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions