Files
Workstation-Setup/AUDIT-PHASE-1-FINDINGS.md
T
2026-09-28 17:39:45 -07:00

30 KiB

AUDIT PHASE 1: DETAILED FINDINGS

Automotive Workstation Setup Repository
Audit Date: 2026-09-09
Scope: PowerShell 5.1+ / PS7 compatibility, JSON schema consistency, configuration traceability


EXECUTIVE SUMMARY

The automotive workstation setup repository demonstrates a solid foundational design with working implementations for profile-based configuration, portable application management, and cross-partition isolation. However, it contains several architectural inconsistencies, validation gaps, redundant code patterns, and security considerations that require remediation before production deployment.

Key findings:

  • ✅ Multi-profile architecture is sound (DailyTech & Tuning, ODIS & XENTRY, PIWIS & ISTA)
  • ✅ Configuration-driven approach using JSON is maintainable
  • ✅ Error handling with Invoke-Safe continues after non-critical failures
  • ✅ Logging and result tracking is comprehensive
  • ⚠️ Path derivation inconsistencies between $ProfileInstallerRoot hard-code and $SharedPortableRoot usage
  • ⚠️ MVCI PRO duplicate definitions in Installers and CommunicationSoftware sections
  • ⚠️ GitHub regex patterns may match unintended assets (WinMerge, CyberChef, Ghidra)
  • ⚠️ HTTP downloads (MVCI PRO, some HWiNFO) lack explicit trust-chain documentation
  • ⚠️ No profile-specific application filtering (all portable apps on all profiles)
  • ⚠️ No profile-specific Windows settings configuration
  • ⚠️ Minimal validation of downloaded executables (size check, not signature/hash)
  • ⚠️ No structured workspace templates for the three automotive profiles
  • ⚠️ No pending-action tracking for interactive installers

PART 1: POWERSHELL SCRIPT ANALYSIS

1.1 SYNTAX AND LOGIC VERIFICATION

File: Automotive-Workstation-Setup.ps1

1.1.1 Scope and Strict Mode

  • Finding: Script uses Set-StrictMode -Version Latest and $ErrorActionPreference = 'Stop'
  • Status: ✅ PASS — Appropriate for production automation
  • Severity: Informational

1.1.2 Parameter Validation

  • Finding: Parameters defined in lines 49-72:
    • ValidateSet for $WorkstationProfile is correctly limited to 3 options
    • [ValidateRange()] on $DownloadRetryCount (1-10) and $DownloadTimeoutSeconds (30-3600)
    • [switch] parameters default to $false (idempotent)
    • $CreateSharedLinks = $true by default (toggleable)
  • Status: ✅ PASS — Parameters are well-designed

1.1.3 Configuration Loading (Import-SetupConfiguration)

  • Lines: 95-105
  • Issue: Configuration files assumed in .\Config\ directory, no fallback or deployment validation
  • Status: ⚠️ MEDIUM SEVERITY
  • Recommendation: Add configuration path parameter with validation before attempting load:
    [ValidateScript({ Test-Path -LiteralPath (Join-Path $_ 'apps-core.json') })]
    [string]$ConfigPath = (Join-Path $PSScriptRoot 'Config')
    

1.1.4 Profile Selection Logic

  • Lines: 110-114
  • Logic: Uses Where-Object to match profile name; throws if not found
  • Issue: Profile lookup should use hashtable keying for O(1) performance; current approach is O(n)
  • Status: ⚠️ LOW SEVERITY (functional, but inefficient)
  • Finding: Code is correct but could be optimized:
    $Profile = $Configuration.Profiles.Profiles |
        Where-Object { $_.Name -eq $WorkstationProfile } |
        Select-Object -First 1
    if ($Profile.Count -ne 1) { throw ... }
    $Profile = $Profile[0]
    
    This pattern is awkward; recommend direct indexing via hashtable on name.

1.1.5 Drive Letter Hard-Coding

  • Lines: 116-118
  • Critical Issue: $ProfileInstallerRoot is hard-coded as "P:\Install\{0}" -f $Profile.Folder
  • Finding: This directly uses P: instead of deriving from $SharedPortableRoot, creating inconsistency
  • Status: 🔴 HIGH SEVERITY — Path derivation violation
  • Recommendation: Should be:
    $ProfileInstallerRoot = Join-Path $SharedPortableRoot -ChildPath ('Install', $Profile.Folder)
    
    This ensures if P: is unavailable and $SharedPortableRoot falls back to local path, the installer root follows.

1.1.6 Paths Hashtable

  • Lines: 120-145
  • Status: ✅ PASS — Well-structured, all derivable from $LocalRoot
  • Finding: No inconsistencies; paths are consistent and commented

1.1.7 Logging Initialization

  • Lines: 147-165
  • Status: ✅ PASS — Log file and result collection initialized correctly
  • Finding: Timestamp-based naming is appropriate for audit trail

1.1.8 Write-Log Function

  • Lines: 167-176
  • Status: ✅ PASS — Logs to file and console with color coding
  • Finding: No issues; implementation is sound

1.1.9 Invoke-Safe Error Wrapper

  • Lines: 188-199
  • Status: ✅ PASS — Non-critical failures continue execution
  • Finding: This is the correct pattern for resilient automation

1.1.10 Invoke-ReliableDownload

  • Lines: 257-289
  • Issues Identified:
    • Retry logic with exponential backoff is missing; all retries use same timeout
    • No SSL/TLS validation is explicitly documented (relying on PS defaults)
    • Partial file cleanup on retry (uses $partial temp file) ✅
    • No checksum validation against vendor-published hashes
    • No quarantine of failed downloads
    • Missing headers for GitHub API rate limiting (done in Get-GitHubReleaseAsset but not universally)
    • No size validation beyond "< 1KB is bad"
    • No timeout for retry loop (could hang for $DownloadRetryCount * $DownloadTimeoutSeconds)
    • curl.exe fallback requires manual invocation flag; no auto-detection
  • Status: ⚠️ HIGH SEVERITY — Download trust chain incomplete
  • Recommendations:
    1. Add explicit SHA256 validation when vendor publishes checksums
    2. Add Authenticode signature verification for .exe and .msi
    3. Document acceptable HTTP sources (MVCI PRO J2534)
    4. Add quarantine folder for failed downloads

1.1.11 Get-GitHubReleaseAsset

  • Lines: 360-381
  • Issues:
    • Asset regex patterns may be too broad:
      • WinMerge: (?i).*x64.*(portable|exe).*\\.zip$ could match architecture or pre-release variants
      • CyberChef: (?i)^CyberChef(?:_v?|[-_]).*\\.zip$ could match multiple versions or dev builds
      • Ghidra: (?i)^ghidra_.*_PUBLIC_.*\\.zip$ is good, but no version pinning
      • Notepad++: (?i)npp\\..*\\.portable\\.x64\\.zip$ is good
    • When multiple matches occur, script logs "selected largest" but this could be wrong (e.g., including symbols or debug info)
    • No verification that release is stable (vs. pre-release/draft)
    • No rate-limiting headers preserved between calls
  • Status: ⚠️ HIGH SEVERITY — Asset selection non-deterministic
  • Recommendation: Add release channel filter (stable only) and version pinning options

1.1.12 Test-PortableInstallation vs. Install-PortableArchive Post-Validation

  • Lines: 542-550 (test function)
  • Issue: Validation uses only file name matching; does not verify:
    • File is executable (not just has .exe extension)
    • Actual version matches expected
    • Binary integrity (checksum/signature)
  • Status: ⚠️ MEDIUM SEVERITY — Shallow validation
  • Recommendation: Add optional version extraction from file properties or metadata

1.1.13 Path Separator Inconsistencies

  • Finding: Mix of forward and backslash in JSON folder paths
  • Example: "Projects\\Tuning\\Original" in JSON vs. Join-Path calls in script
  • Status: ✅ PASS — PowerShell handles both; JSON backslashes are correct
  • Finding: No issue, but worth documenting

1.2 FUNCTION DESIGN PATTERNS

1.2.1 Backup and Restore Functions

  • Lines: 320-340 (Backup-WorkstationConfiguration, Restore-WorkstationConfiguration)
  • Issues:
    • Backup: Copies from multiple scattered locations; path list is hard-coded
    • Restore: Assumes backup exists; does not restore to original profile-specific destinations
    • Missing: Selective restore (by category), verification of restored files, pre-restore validation
    • Missing: Backup naming convention does not include profile ID
  • Status: ⚠️ MEDIUM SEVERITY — Restore may overwrite unintended locations
  • Finding: Backup format is ZIP; restore simply expands to $Paths.Root, which could corrupt existing config

1.2.2 Inventory Export

  • Lines: 342-354
  • Scope: Installed software, portable apps, VS Code extensions, drivers, USB devices, disk inventory, environment variables, services
  • Status: ✅ PASS — Comprehensive CSV/JSON export
  • Finding: Good baseline for pre/post-setup comparison

1.2.3 Setup Report Generation

  • Lines: 356-366
  • Formats: CSV, JSON, HTML
  • Status: ✅ PASS — Multi-format reporting is helpful
  • Finding: No issues

1.2.4 Test-Administrator

  • Lines: 394-398
  • Status: ✅ PASS — Correct use of WindowsIdentity and WindowsPrincipal

1.2.5 New-Shortcut

  • Lines: 400-421
  • Issues:
    • Validates target exists but does not check if it's executable
    • Working directory defaults to target's parent (good)
    • No validation of icon location
  • Status: ⚠️ LOW SEVERITY — Minor gap in target validation
  • Recommendation: Add -Force option to overwrite existing shortcuts
  • Lines: 423-435
  • Status: ✅ PASS — Creates NTFS junctions, validates target, preserves existing links
  • Finding: Good defensive pattern

1.2.7 Get-PortableInstallRoot

  • Lines: 437-447
  • Status: ✅ PASS — Prefers P:\, falls back to local $Paths.Portable
  • Finding: Correct pattern; logs fallback

1.3 PORTABLE APPLICATION INSTALLATION

1.3.1 Install-PortableArchive

  • Lines: 515-543
  • Strengths:
    • Downloads to staging folder
    • Validates expected executable after extraction
    • Preserves previous version (.previous suffix)
    • Unblocks files on Windows
    • Cleans up staging
  • Issues:
    • FlattenSingleDirectory only works if source has exactly 1 child folder
    • No size range validation (min/max bytes)
    • No archive integrity check (Test-ZipFile or similar)
    • No extraction validation (silent failures possible)
    • No rollback if post-install validation fails
  • Status: ⚠️ MEDIUM SEVERITY — Silent failure possible on extract
  • Recommendation: Add archive integrity test and explicit extraction logging

1.3.2 Install-PortableExecutable

  • Lines: 545-568
  • Strengths:
    • Downloads to .download temporary file
    • Preserves .previous backup
    • Rolls back on failure
  • Issues:
    • No signature verification for .exe
    • No size range validation
    • Size check "64KB minimum" is arbitrary
  • Status: ⚠️ HIGH SEVERITY — No executable signature validation
  • Recommendation: Add Authenticode check for .exe files with known-good publisher validation

1.3.3 New-PortableAppShortcut

  • Lines: 570-581
  • Issues:
    • Searches all of PortableRoot recursively for executable
    • Creates shortcuts in hard-coded Desktop/Start Menu subdirectories
    • Fails if executable not found (no fallback or logging context)
    • No validation that found .exe is the correct one (could find duplicate name in different subfolder)
  • Status: ⚠️ MEDIUM SEVERITY — Recursive search could be ambiguous
  • Recommendation: Add explicit path hints from configuration or validation via version properties

1.4 SHARED PARTITION INITIALIZATION

1.4.1 Initialize-SharedPartition

  • Lines: 583-615
  • Issues:
    • Validates drive letter format with regex but does not validate existence before accessing
    • Warns if not NTFS but allows continuation (good)
    • Attempts to set volume label but does not fail if it cannot
    • No free-space threshold validation
    • No permission check (read/write test)
  • Status: ⚠️ MEDIUM SEVERITY — Should test write permissions before proceeding
  • Recommendation: Add write-test to confirm partition is writable

1.5 WINGET INTEGRATION

1.5.1 Install-WingetPackage

  • Lines: 617-650
  • Logic:
    • Checks if already installed
    • Requests upgrade if available
    • Falls back to install if not present
    • Tries machine scope, then user scope
  • Issues:
    • Exit code -1978335189 hard-coded (specific to older WinGet; may change)
    • No timeout on WinGet operations
    • Silent mode may suppress important warnings
    • WinGet may require system restart; no pending-restart tracking
  • Status: ⚠️ MEDIUM SEVERITY — No restart tracking
  • Recommendation: Add pending-restart detection via $LASTEXITCODE or registry check

1.5.2 Update-WingetSources

  • Not shown in first 650 lines; need to verify later

1.6 PROFILE-SPECIFIC BEHAVIOR

1.6.1 Current Behavior

  • Finding: All three profiles (DailyTech & Tuning, ODIS & XENTRY, PIWIS & ISTA) install:
    • Same core WinGet packages
    • Same portable applications
    • Same VS Code extensions
  • Issue: No profile-specific app filtering
  • Status: ⚠️ MEDIUM SEVERITY — All profiles identical except workspace folder structure
  • Recommendation: Add ProfileAllowList and ProfileDenyList to app configurations

1.6.2 Windows Settings Configuration

  • Finding: Script does not configure Windows settings (power plan, USB suspend, etc.)
  • Status: ⚠️ HIGH SEVERITY — No Windows hardening or tuning per profile
  • Recommendation: Add Windows configuration section with profile-specific policies

1.6.3 Workspace Structure

  • Finding: Profiles create folder structure but no template files or automation for job creation
  • Status: ⚠️ MEDIUM SEVERITY — Manual workspace setup required
  • Recommendation: Add job/project initializer function

PART 2: JSON CONFIGURATION ANALYSIS

2.1 apps-core.json

File: Config/apps-core.json

[
  { "Id": "7zip.7zip", "Name": "7-Zip" },
  { "Id": "voidtools.Everything", "Name": "Everything Search" },
  ...
]

2.1.1 Issues

  • Array structure: Direct array of packages, no root key (unusual for config files)
  • Missing properties:
    • No category field
    • No version constraints
    • No install scope (machine/user)
    • No silent arguments specific to this app
    • No architecture specification (x64/x86)
  • Package validation:
    • Git.Git is present ✅
    • Python.Python.3.13 — verify this ID exists in WinGet catalog (may be Python.Python.3.13 or just Python.Python)
    • All other IDs appear valid
  • Status: ⚠️ LOW SEVERITY — Works but lacks metadata

2.1.2 Duplication Check

  • Finding: No duplicate IDs

2.1.3 Missing Core Packages (Recommendation)

  • Consider if these are needed:
    • Microsoft.WindowsTerminal (currently optional)
    • Platform-specific debuggers for automotive work
    • ripgrep or ag (already in portable apps)

2.2 apps-optional.json

File: Config/apps-optional.json

{
  "Packages": [...9 packages...],
  "VSCodeExtensions": [...10 extensions...]
}

2.2.1 Issues

  • Structure: Has root key Packages and VSCodeExtensions; inconsistent with apps-core.json (which is direct array)
  • Script consumption: Script accesses via $Configuration.OptionalApps.Packages (works but inconsistent naming)
  • Missing Properties: Same as apps-core.json

2.2.2 VS Code Extensions

  • Issue: 10 extensions are hard-coded in JSON but configuration does not specify:
    • VS Code installation source (portable vs. installed)
    • Extension installation method (how script invokes this)
    • Fallback if extension unavailable
  • Status: ⚠️ LOW SEVERITY — Works but lacking resilience

2.2.3 Node.js Inclusion

  • Finding: OpenJS.NodeJS.LTS in optional packages
  • Question: Is this used for any automotive work, or just development? (Should move to profile-specific if only for DailyTech & Tuning)

2.3 portable-apps.json

File: Config/portable-apps.json

[
  { "Name": "Visual Studio Code", "Type": "Zip", "Uri": "...", "Folder": "Development\\VSCode", ... },
  ...
]

2.3.1 Structure

  • Direct array (like apps-core.json)
  • Properties are inconsistent by Type:
    • Zip/GitHubZip: Uri, Folder, Executable, Archive, Flatten, Shortcut
    • GitHubZip/GitHubExe: Repository, Regex (instead of Uri)
    • ChromeForTesting: Platform (instead of Uri)

2.3.2 Type Validation

Type Fields Issues
Zip Uri, Folder, Executable, Archive, Flatten ⚠️ Archive field sometimes empty (PuTTY)
GitHubZip Repository, Regex ⚠️ Regex patterns may be ambiguous
GitHubExe Repository, Regex ⚠️ jq and yq have good patterns
ChromeForTesting Platform ✅ Good (explicit platform)
Exe Uri, Folder, Executable ✅ Good

2.3.3 Critical Issues

Issue: Hardcoded HWiNFO Uri

  • Line: 5 (HWiNFO)
  • Current: "Uri": "https://www.hwinfo.com/files/hwi_852.zip"
  • Problem: Version hardcoded (852); site may serve newer version at same URL
  • Status: 🔴 CRITICAL — Version pinning violated
  • Recommendation: Either pin to specific version URL or use dynamic resolution (scrape site or GitHub release)

Issue: WinMerge Regex Ambiguity

  • Pattern: (?i).*x64.*(portable|exe).*\\.zip$
  • Problem: Could match:
    • WinMerge-2.16.0-x64-portable-setup.zip ✅
    • WinMerge-2.16.0-x64-exe-portable.zip ✅
    • Unintended debug/symbol variants
  • Status: ⚠️ HIGH SEVERITY — Ambiguous selection
  • Recommendation: Tighten to (?i)WinMerge.*x64.*portable\\.zip$

Issue: CyberChef Regex Ambiguity

  • Pattern: (?i)^CyberChef(?:_v?|[-_]).*\\.zip$
  • Problem: Matches any version; no stable release filter
  • Status: ⚠️ MEDIUM SEVERITY
  • Recommendation: Add stable release filter (skip -dev, -pre, etc.)

Issue: Ghidra Regex

  • Pattern: (?i)^ghidra_.*_PUBLIC_.*\\.zip$
  • Status: ✅ GOOD — Explicitly filters PUBLIC releases
  • Finding: No issues

Issue: Google Chrome for Testing

  • Type: ChromeForTesting
  • Handler: Requires special code (Get-ChromeForTestingAsset)
  • Status: ✅ GOOD — Explicit handling

2.3.4 Missing Properties in Portable Apps

  • No ProfileAllowList — all apps on all profiles
  • No ProfileDenyList
  • No VersionPolicy (stable/latest/pinned)
  • No MinimumBytes validation (only HWiNFO has this)
  • No ExpectedPublisher for signature validation
  • No ExpectedSHA256 for hash validation
  • No Dependencies (e.g., Ghidra depends on Java)
  • No PATHEligible flag (e.g., jq, yq should add to PATH)

2.4 automotive-resources.json

File: Config/automotive-resources.json

2.4.1 CRITICAL ISSUE: MVCI PRO Duplication

Problem:

"Installers": [
  { "Name": "MVCI PRO J2534", "FileName": "MVCI_PRO-J2534.exe", ... },
  ...
],
"CommunicationSoftware": [
  { "Name": "MVCI PRO J2534", "FileName": "MVCI_PRO-J2534.exe", ... },
  ...
]
  • Same application in two arrays
  • Different usage: Installers used with -InstallXhorseSoftware, CommunicationSoftware used with -InstallCommunicationSoftware
  • Different behavior: Installers launched directly; CommunicationSoftware extracted and launcher is called
  • Issue: Confusing; unclear which parameter triggers what
  • Status: 🔴 CRITICAL — Duplicate entries with different behavior
  • Recommendation: Consolidate into single definition with behavior flag

2.4.2 HTTP vs HTTPS

  • MVCI PRO J2534: http://dl.xhorse.com/... (HTTP only!)
  • Status: ⚠️ HIGH SEVERITY — Unencrypted download
  • Recommendation: Document explicit exception; add warning log; consider SHA256 validation mandatory

2.4.3 Hardcoded Xhorse URLs

  • Manuals: Hardcoded URLs (subject to change)
  • Installers: Hardcoded CDN URLs
  • Status: ⚠️ MEDIUM SEVERITY — Maintenance burden
  • Recommendation: Maintain separate xhorse-resources.json with version history

2.4.4 References Section

  • Finding: Contains GitHub references (good documentation)
  • Status: ✅ PASS — Helpful context

2.5 folder-structure.json

File: Config/folder-structure.json

2.5.1 Structure

{
  "Local": [...],
  "SharedData": [...],
  "SharedPortable": [...]
}

2.5.2 Issues

  • Property Naming: Local, SharedData, SharedPortable are inconsistent with usage in script
    • Script loads as $Configuration.Folders and accesses $Configuration.Folders.Local
    • No clear mapping to drive letters (C:, S:, P:)
  • Finding: Folders referenced but script doesn't appear to iterate all of them (verify in full script read)
  • Status: ⚠️ LOW SEVERITY — Naming could be clearer

2.5.3 Folder Count

  • Local: 28 subfolders
  • SharedData: 23 subfolders
  • SharedPortable: 27 subfolders
  • Total: 78 folders to create
  • Status: ✅ PASS — Comprehensive but maintainable

2.6 shortcuts.json

File: Config/shortcuts.json

2.6.1 Structure

{
  "Folders": [
    { "Name": "Automotive Workspace", "PathKey": "Root" },
    ...
  ],
  "Sysinternals": [
    { "Name": "Process Explorer", "File": "procexp64.exe" },
    ...
  ],
  "CommercialAutomotiveTools": [
    "MultiPROG", "WinOLS", ...
  ]
}

2.6.2 Issues

  • PathKey usage: Script must map "PathKey" values to $Paths hashtable keys

  • Finding: No validation that PathKey exists in $Paths

  • Status: ⚠️ LOW SEVERITY — Should validate at startup

  • CommercialAutomotiveTools: List of strings without structure

  • Usage: Script must match these names to installed apps

  • Status: ⚠️ MEDIUM SEVERITY — Fragile matching logic

  • Recommendation: Convert to objects with properties (executable name, shortcut name, etc.)

2.6.3 Sysinternals

  • Good: Explicit file names for each tool
  • Issue: No validation that files exist in Sysinternals installation
  • Status: ⚠️ LOW SEVERITY — Would fail silently if Sysinternals missing

2.7 workstation-profiles.json

File: Config/workstation-profiles.json

2.7.1 Structure

{
  "Profiles": [
    { "Name": "DailyTech & Tuning", "Folder": "DailyTech-Tuning", "Focus": "..." },
    ...
  ]
}

2.7.2 Issues

  • Minimal Properties: Only Name, Folder, Focus
  • Missing: Should also include:
    • AllowedApps / DeniedApps (app filtering)
    • WindowsSettings (profile-specific power plan, etc.)
    • WorkspaceTemplate (default folders to create)
    • PrimaryTools (key apps for this profile)
    • Enabled flag (for deprecation)
  • Status: ⚠️ MEDIUM SEVERITY — Profiles lack customization hooks
  • Recommendation: Extend schema

PART 3: INTEGRATION ANALYSIS

3.1 Script-to-JSON Mapping

Script Variable JSON Source Notes
$Configuration.CorePackages apps-core.json (entire array) ✅ Direct array
$Configuration.OptionalApps apps-optional.json (root object) ⚠️ Inconsistent with core
$Configuration.PortableApps portable-apps.json (entire array) ✅ Direct array
$Configuration.Folders folder-structure.json (root object) ⚠️ Weak naming
$Configuration.Shortcuts shortcuts.json (root object) ✅ Consistent
$Configuration.Profiles workstation-profiles.json (root object) ✅ Consistent
$Configuration.Automotive automotive-resources.json (root object) ✅ Consistent

3.2 MISSING FEATURES

3.2.1 Profile-Specific Applications

  • Gap: No mechanism to install different apps per profile
  • Need: ODIS & XENTRY should have VAG/Mercedes diagnostic tools; PIWIS & ISTA should have Porsche/BMW tools
  • Current: All profiles install identical apps
  • Status: 🔴 CRITICAL — Profiles are not truly differentiated

3.2.2 Windows Configuration

  • Gap: No Windows settings configured (power plan, USB suspend, etc.)
  • Finding: Notes mention this in IMPROVEMENT-REPORT.md or similar?
  • Status: 🔴 CRITICAL — No Windows hardening
  • Recommendation: Add Windows configuration section

3.2.3 Workspace Templates

  • Gap: Folders are created but no template files, metadata, or automation
  • Status: ⚠️ HIGH SEVERITY — Manual job setup required
  • Recommendation: Add job initializer function

3.2.4 Pending Action Tracking

  • Gap: Interactive installers (MVCI PRO, Autel, Xhorse) are launched but no tracking of completion
  • Status: ⚠️ HIGH SEVERITY — No resume-after-interactive functionality
  • Recommendation: Add pending-action JSON file

3.2.5 Restart Handling

  • Gap: WinGet installations may require restart; no detection or resume logic
  • Status: ⚠️ MEDIUM SEVERITY — User must manually rerun
  • Recommendation: Add pending-restart detection

3.2.6 Hash and Signature Validation

  • Gap: Downloads recorded (via Write-DownloadHash) but not validated
  • Status: 🔴 CRITICAL — Security risk
  • Recommendation: Add trusted-hash and Authenticode validation

3.2.7 Version Pinning

  • Gap: Most apps use "latest" (no version pinning)
  • Status: ⚠️ MEDIUM SEVERITY — Unpredictable updates
  • Recommendation: Add version policy configuration

3.2.8 Rollback Mechanisms

  • Gap: Limited rollback (portable app .previous backup exists but no structured rollback plan)
  • Status: ⚠️ MEDIUM SEVERITY — No disaster recovery
  • Recommendation: Add full rollback function

PART 4: SECURITY ANALYSIS

4.1 Download Trust Chain

4.1.1 HTTPS vs HTTP

App Protocol Status
Visual Studio Code HTTPS ✅
Sysinternals HTTPS ✅
PuTTY HTTPS ✅
WinHex HTTPS ✅
HWiNFO HTTPS ✅
Notepad++ (GitHub) HTTPS ✅
MVCI PRO J2534 HTTP 🔴 CRITICAL
Xhorse Multi-PROG HTTPS ✅
Xhorse VVDI-MLB HTTPS ✅
Autel Maxi PC Suite HTTPS ✅

Critical Finding: MVCI PRO J2534 uses unencrypted HTTP

4.1.2 Signature Validation

  • Current: None
  • Recommended: All .exe files should validate Authenticode signature
  • Status: 🔴 CRITICAL — No executable validation

4.1.3 Hash Validation

  • Current: Hashes calculated and logged but not validated
  • Recommended: Provide trusted hashes (vendor-published or pinned)
  • Status: 🔴 CRITICAL — Hashes logged but not checked

4.1.4 GitHub Release Asset Selection

  • Risk: Ambiguous regex patterns could select wrong assets
  • Example: WinMerge pattern (?i).*x64.*(portable|exe).*\\.zip$ could match pre-release builds
  • Status: ⚠️ HIGH SEVERITY

4.2 Executable Validation

4.2.1 Current Checks

  • File exists (after extraction or download)
  • File size > 64KB (minimum)
  • Archive extracts successfully

4.2.2 Missing Checks

  • Authenticode signature verification
  • Expected publisher validation
  • Version extraction and comparison
  • Hash verification against trusted value
  • No malware scan integration

4.2.3 Recommendation

Implement layered validation:

1. HTTPS only (exception list for HTTP if required)
2. File size sanity check (min/max)
3. Archive integrity (if ZIP/7z)
4. Expected executable exists
5. [Optional] Hash verification
6. [Optional] Authenticode signature
7. [Optional] Malware scan (Windows Defender)

PART 5: IDEMPOTENCY AND RECOVERY

5.1 Idempotency Analysis

5.1.1 Second Run Behavior

  • WinGet packages: Checks if installed; skips if present; requests upgrade
  • Portable apps: Validates expected executable; skips if present (unless -ForcePortableUpdates)
  • Shared partitions: Attempts to set label; continues if fails
  • Shortcuts: Overwrites existing
  • Junctions: Skips if link already exists

Status: ✅ MOSTLY IDEMPOTENT — Second run is safe

5.1.2 Gaps

  • Folders are recreated even if they exist (benign)
  • Backup/restore don't check if already done
  • PATH modifications could be duplicated (need to check script)

5.2 Recovery Capabilities

5.2.1 Backup

  • Created on run (if Backup-WorkstationConfiguration is called)
  • Stores config, VS Code settings, PowerShell profile, etc.
  • Compressed as ZIP

5.2.2 Restore

  • Restores latest backup
  • Extracts to $Paths.Root (could overwrite)
  • Issue: No selective restore by category

5.2.3 Rollback

  • Portable apps have .previous backup
  • No rollback for WinGet apps (would require manual uninstall/reinstall)
  • No rollback for Windows settings (no settings configured yet)

PART 6: TESTING AND VALIDATION

6.1 Current Test Coverage

  • No Pester test suite
  • No configuration validation before execution
  • No dry-run / -WhatIf mode

6.2 Gaps

  • No schema validation for JSON files
  • No duplicate ID checking
  • No path traversal detection
  • No circular dependency checking

SUMMARY TABLE: ALL FINDINGS

Severity Count Category Examples
🔴 CRITICAL 5 Path derivation, MVCI duplication, HTTP downloads, Executable validation, Windows config $ProfileInstallerRoot hard-code, MVCI in two arrays, MVCI HTTP, no signature checks, no OS settings
🔴 HIGH 8 Download trust, Profile filtering, Workspace templates, Regex ambiguity, Restart tracking Asset selection ambiguous, all profiles identical, no job templates, no restart handling
⚠️ MEDIUM 12 Backup restore, Archive validation, Regex specificity, Version pinning, Recovery Limited rollback, no archive integrity check, WinMerge/CyberChef regex, latest versions only
⚠️ LOW 8 Naming consistency, Parameter validation, Structure inconsistency apps-optional.json root key inconsistent, config path not validated

NEXT STEPS

This audit findings document completes Phase 1.

Next: Phase 2 will propose an improved architecture, including:

  1. Refined configuration schema
  2. Profile-specific application policies
  3. Windows settings framework
  4. Workspace template system
  5. Download trust chain improvements
  6. Restart and pending-action handling
  7. Testing and validation framework

Expected Deliverables (Phase 2+):

  • Redesigned PowerShell script (addressing all Critical/High findings)
  • Extended JSON configuration files
  • JSON schema files (for validation)
  • Pester test suite
  • Migration and rollback instructions