Skip to content

Complete modernization of PowerShell module with security enhancements#7

Closed
dutch2005 wants to merge 93 commits intolazywinadmin:masterfrom
dutch2005:master
Closed

Complete modernization of PowerShell module with security enhancements#7
dutch2005 wants to merge 93 commits intolazywinadmin:masterfrom
dutch2005:master

Conversation

@dutch2005
Copy link
Copy Markdown

LazyWinAdmin GUI - Modernized (2026 Standard)

LazyWinAdmin is a comprehensive PowerShell-based management tool for Windows administrators. Originally released in 2012, this project has been completely modernized to 2026 standards, transforming from a monolithic WinForms script into a modular, thread-safe, and high-performance application.

🚀 2026 Modernization Highlights

The project has undergone a complete architectural overhaul to meet modern enterprise standards:

  • Modular Architecture: Converted 13,000+ lines of code into a structured PowerShell Module (LazyWinAdminModule).
  • Modern UI (WPF): Replaced legacy WinForms with a responsive Windows Presentation Foundation (WPF) interface using externalized XAML.
  • Async Execution (Runspaces): Implemented multi-threading via PowerShell Runspaces. Remote operations (CIM, AD, Ping) no longer freeze the UI.
  • PowerShell 7+ Optimization: Fully compatible with PowerShell 7.4+ (Core) while maintaining legacy compatibility where possible.
  • CIM over WMI: Migrated all legacy Get-WmiObject calls to the modern, faster, and more secure Get-CimInstance (WinRM/WSMan).
  • Unit Testing: Integrated Pester for core business logic validation.
  • CI/CD: Automated testing via GitHub Actions.

🛠️ Requirements

  • PowerShell 7.4+ (Recommended) or Windows PowerShell 5.1.
  • Windows 10/11 or Windows Server 2019/2022/2025.
  • Administrative permissions on targeted systems.
  • WinRM enabled on remote targets (for CIM operations).

📁 Project Structure

  • LazyWinAdminModule/: The core PowerShell module.
    • Public/: User-facing functions (e.g., Start-LazyWinAdmin).
    • Private/: Internal helper functions and modernized business logic.
    • UI/: WPF/XAML definitions for the modern interface.
    • Tests/: Pester unit tests.
  • Media/: Original and updated project assets.
  • docs/: Modernization plans and migration guides.

🔧 Getting Started

To launch the modernized GUI:

# Import the module
Import-Module ./LazyWinAdminModule/LazyWinAdmin.psd1

# Start the application
Start-LazyWinAdmin

✨ Key Features

  • Real-time Connectivity: Non-blocking Ping and WsMan testing.
  • Hardware/Software Inventory: Modernized data collection for disks, motherboard, BIOS, and installed apps.
  • Service Management: Responsive service query, start, stop, and restart.
  • Active Directory Integration: Modernized AD object lookup and description management.
  • Security: RDP status management and remote registry tools.

📜 Contributions

Contributions are welcome! Please ensure all new logic includes Pester tests and adheres to the modular module structure.


Modernized by the LazyWinAdmin Community.

Lovable and others added 30 commits January 1, 2025 00:00
Implemented bilingual IT Adventures blog with dark tech design:
- Added hero image asset
- Created AboutSection with multilingual skills and stats
- Implemented BlogSection, NewsletterSection, Navbar, Hero, Footer
- Added LanguageContext for NL/EN translations
- Integrated Tailwind design tokens and responsive components
- Updated index.html and app structure to support the new pages

X-Lovable-Edit-ID: edt-40cb6f45-e272-48bd-a0c8-f1503becccd5
Edited UI in Lovable
Set up Supabase backend and migrations for blog, messages, and auth; added admin UI, blog detail routing, and frontend DB-backed data fetching. Also implemented cloud-backed contact form, profile placeholder, and translations integration.

X-Lovable-Edit-ID: edt-73c9fc5e-3bb8-4246-9b1d-3cf4797e3abc
Edited UI in Lovable
Implemented a rich Markdown editor for blog content:
- Added MarkdownEditor component with toolbar (Bold, Italic, Headings, Code, Lists, Links, HR) and live preview rendering.
- Wired MarkdownEditor into Admin.tsx for NL/EN content fields.
- Replaced NL/EN content textareas with MarkdownEditor instances.
- Fixed NewsletterSection section id to newsletter and updated related usage.
- Updated BlogPost rendering to better handle MD-to-HTML conversion (internal rendering already present).

Also granted admin access and adjusted About/CV integration scaffolding remains (no direct CV/photo changes in this commit).

X-Lovable-Edit-ID: edt-908ef291-0258-4d39-8c4d-68c8021e040d
Edited UI in Lovable
Migrate emails to obfuscated dutch2005@xtremeweb.xyz, update LinkedIn URL, adjust certifications to only include Microsoft 365 Certified and 3CX V20, and adapt sign-in placeholder. Also tweak UI for email obfuscation and tighten certifications grid layout.

X-Lovable-Edit-ID: edt-d792fa60-c724-479a-bdc8-f94d34ea1be9
Migrate to a new external Supabase client by adding a dedicated client module using VITE_EXTERNAL_SUPABASE_URL and VITE_EXTERNAL_SUPABASE_ANON_KEY, and replace all imports of the old auto-generated client with the new db client. Added centralized db client and updated components/pages to use it.

X-Lovable-Edit-ID: edt-d15dd87d-4892-4f0f-8208-2a7b30d6e14f
- Navbar: replace <a href="#..."> with React Router <Link to="/#..."> so nav works from /blog/:slug pages
- Navbar: swap Terminal icon for /logo.svg img
- BlogPost: add linkify() helper — auto-links bare URLs and markdown [text](url) in post content
- index.html: add GA4 tag (G-0E07DMNCSG), update favicon to logo.svg
- index.html: add commented Skimlinks monetization script (ready to activate)
- public/logo.svg: new MM logo (dark bg, cyan border/text) for favicon + navbar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.html: implement Google Consent Mode v2 defaults before gtag.js loads
  - all signals denied by default (ad_storage, analytics_storage,
    ad_user_data, ad_personalization)
  - wait_for_update: 500ms on first visit so React banner can fire update
  - restores previously stored consent from localStorage on repeat visits
- CookieConsent.tsx: bilingual (NL/EN) GDPR banner fixed at bottom
  - "Alles accepteren" → grants all 4 consent signals + saves to localStorage
  - "Alleen functioneel" / X → denies all signals + saves to localStorage
  - expandable "Meer info" section explaining what each cookie type does
  - matches site dark theme with cyan primary accent
- App.tsx: mount <CookieConsent /> inside LanguageProvider so NL/EN works

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Navbar: replace <Link to="/#..."> with scrollToSection() click handler
  - If already on /: calls scrollIntoView() or scrollTo(top) directly
  - If on /blog/:slug or any other page: navigate('/', { state: { scrollTo } })
  - Converted nav items and logo to <button> elements
- Index.tsx: add useEffect on location.key to consume state.scrollTo and
  scroll to the right section after arriving from another page
- BlogSection, AboutSection, ContactSection: add scroll-mt-16 so the 64px
  fixed navbar doesn't overlap the section heading on scroll target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React/deps:
- react + react-dom: 18.3 → 19.2.4
- @types/react + @types/react-dom: 18.x → 19.2.x
- @types/node: 22.x → 24.x
- react-day-picker: 8.10 → 9.13.2 (v9 API)
- Add engines: { node: ">=24.0.0", npm: ">=10.0.0" }

calendar.tsx: rewrite for react-day-picker v9 API
- Rename classNames keys (caption→month_caption, nav_button_*→button_*,
  head_row→weekdays, head_cell→weekday, row→week, cell→day, day→day_button,
  day_*→* for selected/today/outside/disabled/range_*)
- Replace IconLeft/IconRight components with single Chevron component

README: full rewrite with tech stack table, versions, project structure,
setup instructions, deployment notes and React 19 compat notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Edited UI in Lovable
Refactored admin into a sidebar-driven dashboard with multiple panels:
- Added AdminSidebar with navigation and logout
- Implemented Dashboard, BlogPosts, Messages, Subscribers, SocialShare, Security, and Settings panels
- Replaced Admin.tsx with sidebar-based routing and authentication guard
- Introduced admin_audit_log table and related policies
- Updated components to use new external Supabase client and expanded admin features (export, social sharing, security, activity logs)

X-Lovable-Edit-ID: edt-a4db129b-9f58-42e9-9635-0b07b3bfbbfc
Edited UI in Lovable
Add ReadingProgress and RelatedPosts integration; implement SEOHead usage, TableOfContents, and open-graph JSON-LD scaffolding across BlogPost. Update App.tsx to use HelmetProvider; refine BlogPost to include TOC, headings, and improved navigation.

X-Lovable-Edit-ID: edt-b635e78a-e7a6-4593-bed1-1f7cf6ff6c9c
Add DELETE policies for contact_messages and newsletter_subscribers,
add email format constraints, replace 404 console log, and update NewsletterSection to use Zod-based email validation.

X-Lovable-Edit-ID: edt-2ea8cf92-c753-4829-9678-091ce7db965f
Edited UI in Lovable
dutch2005 and others added 21 commits March 1, 2026 02:16
* 🧹 [Code Health] Unify Supabase client usage

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* 🧹 [Code Health] Unify Supabase client usage

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
🎯 What: The vulnerability fixed
A Stored Cross-Site Scripting (XSS) vulnerability existed in src/pages/BlogPost.tsx where user-supplied content (through the linkify function) was being rendered directly via dangerouslySetInnerHTML without proper sanitization.

⚠️ Risk: The potential impact if left unfixed
Malicious actors could inject arbitrary JavaScript into blog posts or list items. When a user views the compromised post, the script would execute in their browser context. This could lead to session hijacking, unauthorized actions on behalf of the user, or sensitive data theft. The blast radius could be high since any visitor reading the infected blog post would be affected.

🛡️ Solution: How the fix addresses the vulnerability
The output of linkify(para) and linkify(item.slice(2)) is now passed through DOMPurify.sanitize() before being injected using dangerouslySetInnerHTML. DOMPurify safely strips out any malicious script tags and event handlers while preserving safe HTML, ensuring that the content is rendered securely.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
💡 **What:** Memoize the creation and stringification of the \`structuredData\` object using \`useMemo\`.
🎯 **Why:** Previously, the \`structuredData\` object was allocated and \`JSON.stringify\` was called on every render of the \`SEOHead\` component. While \`JSON.stringify\` on a small object is relatively fast, doing so on every render introduces unnecessary CPU overhead and generates garbage that needs to be collected.
📊 **Measured Improvement:** In a 1,000,000 iteration synthetic benchmark, allocating and stringifying the object repeatedly took ~1075ms. Memoizing the pre-stringified value took ~3ms for the same number of accesses. This eliminates redundant operations on each component render, making the component functionally lighter.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
* feat: implement remaining TODOs for mikemaze.nl

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* fix: resolve review feedback

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* fix: correctly capture insertedData

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* chore: acknowledge PR comments

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
* 🧪 Add tests for cn utility function

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* 🧪 Add tests for cn utility function

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* 🧪 Add tests for cn utility function

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* chore: remove bun.lockb from tracking (project uses npm)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add error test for useLanguage hook

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

* Fix PR comments - merge conflicts

Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: dutch2005 <3473864+dutch2005@users.noreply.github.com>
supabaseClient.ts exports 'db', not 'supabase'. Use alias import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Convert Admin, AdminLogin, Unsubscribe to React.lazy() + Suspense
- Main bundle: 932KB -> 473KB gzip (289KB -> 148KB)
- Admin chunk loads only when visiting /admin
- Update docs/todo.md with full completed/pending state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…qyzfbpui)

Both Supabase clients now consistently prefer VITE_EXTERNAL_SUPABASE_URL
so all code targets the active Mike Maze IT Adventures project.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Live table was missing unsubscribe_token (created before this column
was added to the schema). Applied via Management API and committed
migration for future reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Duolingo-style re-engagement at 1w/1m/3m/6m/1y with escalating
personal tone. Pixel + URL token tracking, pg_cron daily trigger,
single send-reminders edge function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8 tasks: schema migration, track-open/track-visit/send-reminders
edge functions, frontend beacon, subscribe update, pg_cron schedule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents: deployment status, re-engagement system design,
PR #21 two blockers (pg_cron placeholder, BlogPost.tsx missing hook).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ders, pg_cron)

Implements Duolingo-style re-engagement emails at 1w/1m/3m/6m/1y intervals with pixel + URL token tracking and GDPR-aware consent handling.
Supabase auto-injects SUPABASE_SERVICE_ROLE_KEY in a different format than
the legacy JWT stored locally. Switch to a dedicated CRON_SECRET env var
for pg_cron authentication, which is simpler and more explicit.

Deploy: npx supabase secrets set CRON_SECRET=<value>
pg_cron job updated to use Bearer <CRON_SECRET> in Authorization header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All re-engagement tasks done. Two manual items remain:
publish content drafts and verify OG image live.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upload og-image.jpg to public Supabase Storage bucket (site-assets)
- Add SEOHead to Index.tsx with the stable Storage CDN URL
- Avoids Lovable deployment 404 for /og-image.jpg

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dutch2005 and others added 6 commits March 5, 2026 19:59
…, and full Pester v5 test suite

Core module changes:
- Bump module version to 1.1.0 and raise PowerShellVersion to 7.4; remove ThreadJob from
  RequiredModules (inbox in PS 7.4+) to fix Import-Module failure on PS 7.6
- ApplicationState: add CimSessions synchronized cache, CloudConnected flag, EvictCimSession
  method, and proper RunspacePool.Close() in Dispose()
- Start-LazyWinAdmin: remove -RunspacePool from Start-ThreadJob (PS 7 inbox stub lacks the
  param); wrap job dispatch in try/catch to prevent ShowDialog crash on job-start failure;
  add RequireCloudSession and RequireComputerName pre-flight guards so unavailable features
  show a friendly status message instead of crashing
- Private functions: fix .NET 7+ RegexParseException caused by backslash-underscore in
  character classes (Get-EntraIdentity, Get-IntuneDevice, Get-ComputerADInfo); replace
  Win32_Product with StdRegProv registry enumeration in Get-ComputerSoftware; split RDP
  registry and firewall steps in Set-ComputerRDP; migrate Azure queries to Search-AzGraph

Test suite (Pester v5, 176 tests, 0 failures):
- Integrity.Tests.ps1: file structure, module manifest, XAML named-control validation,
  ApplicationState class lifecycle
- Functions.Tests.ps1: all 16 private functions covered; WinRM probe at script top-level
  for discovery-phase -Skip; fake CimInstance::new pattern to satisfy Invoke-CimMethod
  proxy type constraint without real WMI access; AD command stubs for RSAT-free machines
- Run-Tests.ps1: runner with Pester auto-install, -Suite and -Output params, summary table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-feature access hints

- XAML: add lblAdminStatus + btnRestartAdmin to status bar; add tooltips to hardware,
  RDP, and registry write/delete buttons explaining which operations need admin rights
- Start-LazyWinAdmin: detect elevation at startup via WindowsPrincipal.IsInRole;
  show green 'Administrator' or amber '(!) Standard User' label in status bar;
  hide Restart button when already elevated; wire button to relaunch pwsh elevated
  via Start-Process -Verb RunAs then close the current window
- Add $AdminHint helper: when a hardware/RDP/registry operation returns null or an
  error AND the process is not elevated AND the target is the local machine, append
  a clear message pointing the user to 'Restart as Admin' instead of silently showing
  empty results
- Integrity.Tests.ps1: add lblAdminStatus and btnRestartAdmin to XAML control coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- .claude/launch.json: dev launch configurations for the GUI and Pester
  test runner (pwsh-based; no web server port since this is a WPF app)
- lazywinadmin.speq: project specification/contract file
- Remove .lovable/plan.md — project is no longer hosted on Lovable.dev

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
$AppendOutput and $SetBusy now use Dispatcher.CheckAccess() to execute
directly when already on the UI thread, avoiding re-entrant DispatcherFrame
pushes. The Register-ObjectEvent action now uses Dispatcher.InvokeAsync so
the event thread is non-blocking and OnCompleted callbacks never nest inside
a synchronous Invoke call. The try/catch in Invoke-AsyncAction also drops an
unnecessary Dispatcher.Invoke since it runs on the UI thread directly.

Also adds CLAUDE.md with project conventions and updates README.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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