WinUtil is a PowerShell utility with a graphical window that gathers the jobs people do on a fresh Windows install. It bulk-installs apps through winget or Chocolatey, turns off telemetry, activity history and consumer features, can remove OneDrive or Edge if you ask, applies common fixes and sets how Windows Update behaves. You paste one line into an admin terminal and choose what you want from its tabs.
It earns its place by being open about what it does. Every tweak is defined in the repository's config files, and the three presets (Standard, Minimal and Advanced) are listed in config/preset.json, so you can read the exact changes before applying them. Standard and Advanced create a restore point first. It is MIT-licensed, maintained by Chris Titus with 186 contributors, and has about 65,000 stars, at release 26.10.07 when scanned.
The honest limit is the nature of the job: these are system-wide changes made as Administrator. Some tweaks, such as removing Edge or disabling services, can break things other software expects, and the Minimal preset does not make a restore point. A faster .NET version is sold separately; the PowerShell script on GitHub is the free one.
- Repository: github.com/ChrisTitusTech/winutil
- Licence: MIT (MIT License)
- Language: PowerShell. Stars: 65K. Forks: 3,808. Last push: Oct 26, 2026.
- Scan: safe, Oct 26, 2026, commit 2f0bdc8
Who it is for
People setting up a new Windows PC who want it quieter and less cluttered, IT technicians repeating the same installs on many machines, and anyone who would otherwise work through a long debloat guide by hand.
Getting started
1. Open PowerShell or Terminal as Administrator and run the stable branch
irm https://christitus.com/win | iex2. Or apply a preset (Standard, Minimal or Advanced) without clicking through the window
& ([ScriptBlock]::Create((irm https://christitus.com/win))) -Preset StandardThe one-liner downloads the script from christitus.com and runs it at once with admin rights. To see what a preset changes before you run it, read config/preset.json in the repository; each tweak is defined in config/tweaks.json. Have a backup or restore point before trying the Advanced tweaks.
Safety scan
We cloned ChrisTitusTech/winutil at commit 2f0bdc8 on Oct 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- No secrets, no committed binaries and no bare-IP URLs across about 27,000 lines of PowerShell.
- The 3 pattern hits are PowerShell -EncodedCommand calls. Two, in scripts/start.ps1 at lines 201 and 217, relaunch WinUtil itself with admin rights; the encoded command is built from its own script path and your parameters by New-WinUtilElevationCommand, a normal way to carry arguments through a UAC prompt. The third is in a Pester test, pester/sanity.Tests.ps1.
- The 16 installer-style files are the app's own install engine and its tests: functions that set up winget and Chocolatey (from community.chocolatey.org) and install the programs you tick. None uses sudo-style tricks, pipes a download into a shell or edits a shell profile.
- 50 known advisories, none in the script you run. 41 are in tools/title-screen/uv.lock, a Python helper with an old Pillow that renders the README's title screenshot, and 9 in docs/package-lock.json, the documentation site. WinUtil itself has no package dependencies.
- 12 workflows. One uses pull_request_target (label-pr.yaml), and it only reads the pull request's description to add labels; it never checks out the submitted code. None of the 8 third-party actions is pinned to a commit. Security policy, Dependabot, contributing guide and code of conduct present.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | None found. |
| Suspicious code | 3 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 16 installer scripts |
| Committed binaries | None. |
| CI workflows | 12 workflows. 1 uses pull_request_target, none check out the pull request head. 8 of 8 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 20 distinct hosts referenced from source; most often github.com, winutil.christitus.com, schneegans.de, christitus.com. No URLs to bare IP addresses. |
| Known vulnerabilities | 50 advisories across 483 pinned packages: 5 critical, 27 high, 17 moderate, 1 low. docs/package-lock.json: 480 packages, 9 advisories; tools/title-screen/uv.lock: 7 packages, 41 advisories. |
| Project hygiene | Has security policy, automated dependency updates, licence file, contributing guide. Missing CodeQL. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Pattern hits (3)
| Where | Rule | Match |
|---|---|---|
| pester/sanity.Tests.ps1:57 | powershell-encoded | $output = & $windowsPowerShell.Source -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encodedCommand 2>&1 |
| scripts/start.ps1:201 | powershell-encoded | $elevated = Start-Process $powershellCmd -ArgumentList @("-ExecutionPolicy", "Bypass", "-NoProfile", "-EncodedCommand", $elevationCommand) -Verb RunAs -Wait -Pa… |
| scripts/start.ps1:217 | powershell-encoded | Start-Process $processCmd -ArgumentList "$powershellCmd -ExecutionPolicy Bypass -NoProfile -EncodedCommand $elevationCommand" -Verb RunAs |
Installer scripts (16)
- functions/private/Install-WinUtilAPPX.ps1, 97 lines
- functions/private/Install-WinUtilChoco.ps1, 40 lines; talks to community.chocolatey.org
- functions/private/Install-WinUtilProgramChoco.ps1, 117 lines
- functions/private/Install-WinUtilProgramWinget.ps1, 287 lines; talks to api.github.com, learn.microsoft.com
- functions/private/Install-WinUtilWinget.ps1, 30 lines
- functions/private/Start-WinUtilAssetRendering.ps1, 41 lines
- functions/private/Start-WinUtilBackgroundQueue.ps1, 157 lines
- functions/private/Start-WinUtilInstallAppRendering.ps1, 68 lines
- functions/private/Start-WinUtilJob.ps1, 259 lines
- functions/private/Start-WinUtilTabWarmup.ps1, 39 lines
- functions/private/Start-WinUtilUserInterface.ps1, 539 lines; talks to github.com, winutil.christitus.com, www.microsoft.com
- pester/install-rendering.Tests.ps1, 106 lines
- pester/install-workflow.Tests.ps1, 386 lines
- pester/runspace-lifecycle.Tests.ps1, 116 lines
- pester/runspace.Tests.ps1, 340 lines
- scripts/start.ps1, 268 lines; talks to github.com
Worst known vulnerabilities (24 of 50)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-332p-7hrc-92wf | critical | pillow@12.3.0 | Pillow: Heap buffer overflow in _tiffWriteProc when saving TIFF to BytesIO |
| GHSA-5x8w-8jj5-q52h | critical | pillow@12.3.0 | Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution |
| GHSA-8g5g-8vmr-9782 | critical | pillow@12.3.0 | Pillow: Heap buffer overflow in ImagingJpegDecode |
| GHSA-v9pv-5gc8-w2m6 | critical | pillow@12.3.0 | Pillow multi-frame loaders can reuse stale native image storage after mode transitions |
| GHSA-w889-685w-c8fh | critical | pillow@12.3.0 | Pillow: Argument Injection in Ghostscript call, potentially leading into RCE |
| GHSA-ch52-4w7c-c8xp | high | http-cache-semantics@4.2.0 | http-cache-semantics max-stale handling can disclose cross-user cached responses |
| GHSA-wq5f-xc86-pv6w | high | sharp@0.35.4 | sharp : Vulnerability in librsvg dependency CVE-2026-96889 |
| GHSA-xjj6-f33q-929c | high | undici@8.10.2 | undici vulnerable to Denial of Service via unhandled error on excessive content-encodings |
| GHSA-mrhx-c7xc-m3w5 | high | zod@4.6.1 | Zod can exhaust memory while validating oversized constrained collections |
| GHSA-22hg-r758-rrw9 | high | pillow@12.3.0 | Pillow: Infinite loop in MIC n_frames when the embedded TIFF |
| GHSA-27h8-gf7j-v5hc | high | pillow@12.3.0 | Pillow: NULL dereference in ImagingExpand via rank filters on zero-size images |
| GHSA-3j72-p6qg-6xp7 | high | pillow@12.3.0 | Pillow: TarIO negative size field causes infinite CPU-spin loop |
| GHSA-58r4-6rv3-gfh8 | high | pillow@12.3.0 | Pillow: Out-of-bounds access in _imagingmorph on a zero-width image |
| GHSA-5xm2-6676-qcp9 | high | pillow@12.3.0 | Pillow: APNG background disposal allocates full canvas before decompression bomb check |
| GHSA-65qm-jvmh-6pqg | high | pillow@12.3.0 | Pillow: GIF seek keeps rejected size after DecompressionBombError, allowing DoS |
| GHSA-78g4-xqwv-q3g8 | high | pillow@12.3.0 | Pillow: Bundled libopenjp2 2.5.4 in all wheels since 11.1.0 carries OSV-2025-219 heap-buffer-overflow write reachable fr… |
| GHSA-7mwm-ghpf-9vrm | high | pillow@12.3.0 | Pillow: Double free in ImagingScaleAffine on calloc failure via Image.transform |
| GHSA-9v9v-9qvf-h2hj | high | pillow@12.3.0 | Pillow: Heap over-read in WMF/EMF rendering via 16-bit dimension truncation (Windows builds) |
| GHSA-c6hw-5grf-ff96 | high | pillow@12.3.0 | Pillow: OOB read in TiffDecode.c tile and RGBA paths leaks heap memory into pixels |
| GHSA-cp9v-662q-vvx9 | high | pillow@12.3.0 | Pillow: NULL pointer dereference in Image.merge via an unvalidated band 0 |
| GHSA-f2g6-542v-9j58 | high | pillow@12.3.0 | Pillow: Infinite loop in PdfParser.rewrite_pages on page tree Parent cycle |
| GHSA-g4qv-2jxx-769h | high | pillow@12.3.0 | Pillow: PNG eXIf chunk makes EXIF parsing run in quadratic time |
| GHSA-g7cr-3p24-7wf7 | high | pillow@12.3.0 | Pillow: Heap buffer overflow writing JPEG comment after ICC profile fills buffer |
| GHSA-g842-9rc4-69pw | high | pillow@12.3.0 | Pillow: Decompression bomb check bypass in SpiderImagePlugin.seek() leads to unbounded allocation DoS |
Workflows worth a look
- .github/workflows/label-pr.yaml: pull_request_target
By the numbers
| Stars | 65K |
|---|---|
| Forks | 3,808 |
| Contributors | 186 |
| Commits | 1,749 |
| Open issues | 22 |
| Open pull requests | 15 |
| Releases | 47 |
| Latest release | 26.10.07 |
| Licence | MIT |
| Main language | PowerShell |
| Project age | 4 years |
| Last push | Oct 26, 2026 |
| Tracked files | 389 |
| Lines of code | 42.5K |
| Checkout size | 5 MB |
Lines by language: PowerShell 26.8K, Markdown 7,582, JSON 5,137, Python 1,166, YAML 1,145, CSS 491.
Questions
Is WinUtil free?
Yes. The PowerShell script is MIT-licensed and free. A faster .NET version is sold on the author's store and offered to recurring sponsors; you do not need it to use WinUtil.
Is WinUtil safe to run?
The scan found nothing hidden: what it runs is in the repository, readable PowerShell. The risk is in the changes you choose. Start with the Standard preset, which makes a restore point, read a tweak's definition before applying anything from Advanced, and only use the official one-liner or the GitHub release, since copies elsewhere could be altered.
Can I undo WinUtil's changes?
The restore point that Standard and Advanced create is the dependable way back. Some tweaks, such as removing Edge or OneDrive, mean reinstalling that software to reverse them.
Why does WinUtil need Administrator rights?
Its tweaks change system-wide settings, services and installed software, which Windows only allows from an elevated session. If you start it without admin rights, it relaunches itself through a UAC prompt.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
