Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CI

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
test:
name: Test on Windows
runs-on: windows-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install PSScriptAnalyzer
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser

- name: Install Pester
shell: pwsh
run: |
Install-Module -Name Pester -Force -Scope CurrentUser -MinimumVersion 5.0.0 -SkipPublisherCheck

- name: Run PSScriptAnalyzer
shell: pwsh
run: |
$results = Invoke-ScriptAnalyzer -Path ./src -Recurse -Settings PSGallery
if ($results) {
$results | Format-Table -AutoSize
Write-Error "PSScriptAnalyzer found $($results.Count) issue(s)"
exit 1
}
Write-Host "PSScriptAnalyzer: No issues found" -ForegroundColor Green

- name: Run Pester Tests
shell: pwsh
run: |
$config = New-PesterConfiguration
$config.Run.Path = './tests'
$config.Run.PassThru = $true
$config.Output.Verbosity = 'Detailed'
$config.CodeCoverage.Enabled = $false

$result = Invoke-Pester -Configuration $config

if ($result.FailedCount -gt 0) {
Write-Error "Pester tests failed: $($result.FailedCount) failed out of $($result.TotalCount)"
exit 1
}

Write-Host "All tests passed: $($result.PassedCount)/$($result.TotalCount)" -ForegroundColor Green

- name: Validate Module Manifest
shell: pwsh
run: |
$manifestPath = './src/ScriptWhitelistGuard.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop
Write-Host "Module manifest is valid" -ForegroundColor Green
Write-Host " Name: $($manifest.Name)"
Write-Host " Version: $($manifest.Version)"
Write-Host " Author: $($manifest.Author)"
108 changes: 108 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Publish to PowerShell Gallery

on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
publish:
name: Publish Module
runs-on: windows-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Validate Tag Format
shell: pwsh
run: |
$tag = $env:GITHUB_REF -replace 'refs/tags/', ''
if ($tag -notmatch '^v\d+\.\d+\.\d+$') {
Write-Error "Tag format invalid. Expected: v1.0.0, Got: $tag"
exit 1
}
$version = $tag -replace '^v', ''
Write-Host "Publishing version: $version"
echo "MODULE_VERSION=$version" >> $env:GITHUB_ENV

- name: Update Module Version
shell: pwsh
run: |
$manifestPath = './src/ScriptWhitelistGuard.psd1'
$version = $env:MODULE_VERSION

# Read manifest content
$content = Get-Content $manifestPath -Raw

# Update ModuleVersion
$content = $content -replace "ModuleVersion\s*=\s*'[\d\.]+'", "ModuleVersion = '$version'"

# Write back
Set-Content -Path $manifestPath -Value $content -NoNewline

Write-Host "Updated module version to $version"

- name: Install Dependencies
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
Install-Module -Name Pester -Force -Scope CurrentUser -MinimumVersion 5.0.0 -SkipPublisherCheck
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser

- name: Run Tests Before Publishing
shell: pwsh
run: |
$config = New-PesterConfiguration
$config.Run.Path = './tests'
$config.Run.PassThru = $true
$config.Output.Verbosity = 'Detailed'

$result = Invoke-Pester -Configuration $config

if ($result.FailedCount -gt 0) {
Write-Error "Tests failed. Aborting publish."
exit 1
}

- name: Validate Module Manifest
shell: pwsh
run: |
$manifestPath = './src/ScriptWhitelistGuard.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop

Write-Host "Module Manifest Valid" -ForegroundColor Green
Write-Host " Name: $($manifest.Name)"
Write-Host " Version: $($manifest.Version)"
Write-Host " GUID: $($manifest.Guid)"

- name: Publish to PowerShell Gallery
shell: pwsh
env:
PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}
run: |
if (-not $env:PSGALLERY_API_KEY) {
Write-Error "PSGALLERY_API_KEY secret not set. Please configure it in repository secrets."
exit 1
}

$modulePath = './src'

try {
Publish-Module -Path $modulePath -NuGetApiKey $env:PSGALLERY_API_KEY -Verbose -ErrorAction Stop
Write-Host "✓ Successfully published to PowerShell Gallery" -ForegroundColor Green
}
catch {
Write-Error "Failed to publish module: $_"
exit 1
}

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
generate_release_notes: true
files: |
./src/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# PowerShell module artifacts
*.nupkg
*.zip

# Test results
TestResults/
*.trx
*.xml

# User whitelist data (for local testing)
.ps-script-whitelist.json

# IDE and editor folders
.vscode/
.idea/
*.code-workspace

# OS generated files
.DS_Store
Thumbs.db
desktop.ini

# Build output
bin/
obj/
publish/
86 changes: 86 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Changelog

All notable changes to ScriptWhitelistGuard will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2026-01-29

### Added

- **Core Whitelist Management**
- `Add-ScriptWhitelist`: Add or update scripts with SHA256 hash validation
- `Remove-ScriptWhitelist`: Remove scripts from whitelist
- `Test-ScriptWhitelist`: Verify script whitelist status and hash integrity
- `Get-ScriptWhitelist`: List all whitelisted scripts with metadata
- `Repair-ScriptWhitelist`: Convenience command to update hashes after script modifications

- **Interactive Guard System**
- `Enable-WhitelistGuard`: Activate PSReadLine Enter key interception
- `Disable-WhitelistGuard`: Deactivate guard and restore default behavior
- `-Persist` flag: Auto-enable guard in all new PowerShell sessions via profile integration
- `-Unpersist` flag: Remove auto-enable block from profile

- **Whitelist Storage**
- JSON-based persistent storage at `$HOME\.ps-script-whitelist.json`
- Environment variable override: `SCRIPT_WHITELIST_GUARD_STORE` for custom storage paths
- SHA256 hash verification for script integrity

- **PSReadLine Integration**
- Custom Enter key handler with AST-based command parsing
- Selective interception: only external `.ps1` scripts
- Transparent command rewriting: whitelisted scripts execute with `-ExecutionPolicy Bypass`
- Helpful error messages with copy-paste commands for blocked scripts

- **Cross-Platform Support**
- PowerShell 5.1 (Windows PowerShell) compatibility
- PowerShell 7+ (pwsh) support
- Automatic detection of available PowerShell executable

- **Profile Management**
- Idempotent profile block insertion with clear begin/end markers
- Safe removal that preserves user's existing profile content
- Automatic profile file creation if it doesn't exist

- **Testing & Quality**
- Comprehensive Pester test suite (20+ test cases)
- Tests for whitelist operations, hash validation, profile persistence
- GitHub Actions CI/CD workflows
- PSScriptAnalyzer integration for code quality

- **Documentation**
- Comprehensive README with examples and security warnings
- FAQ section covering common scenarios
- Clear explanation of limitations and non-security-boundary nature

### Security Notes

⚠️ **This is NOT a security boundary.** ScriptWhitelistGuard can be easily bypassed by:
- Running `powershell -ExecutionPolicy Bypass` directly
- Executing scripts non-interactively (scheduled tasks, CI/CD)
- Disabling the guard with `Disable-WhitelistGuard`

For true enforcement, use:
- **AppLocker** or **Windows Defender Application Control (WDAC)** on Windows
- **Code signing** with trusted certificates
- **GPO-based** execution policies

This module is designed for **workflow safety** to prevent accidental execution of untrusted scripts, not as malware protection.

## [Unreleased]

### Planned Features
- Optional logging of all script execution attempts
- Integration with Windows Event Log
- Support for wildcard/regex patterns in whitelist paths
- Team whitelist synchronization helpers
- PowerShell 7+ module cache optimization

---

## Release Tags

- `v1.0.0` - Initial public release

[1.0.0]: https://github.com/YourOrg/ScriptWhitelistGuard/releases/tag/v1.0.0
Loading