diff --git a/.config/PSScriptAnalyzerSettings.psd1 b/.config/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000..b1ff2a1 --- /dev/null +++ b/.config/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,13 @@ +@{ + IncludeRules = @( + 'PSAvoidUsingWriteHost', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUseCompatibleSyntax' + ) + Rules = @{ + PSUseCompatibleSyntax = @{ + Enable = $true + TargetVersions = @('7.2','WindowsPowerShell 5.1') + } + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bce5d4e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: CI (PowerShell) + +on: + push: + branches: [ "main", "feat/**" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + lint-and-test: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install modules (PSScriptAnalyzer, Pester) + shell: pwsh + run: | + Set-PSRepository PSGallery -InstallationPolicy Trusted + Install-Module PSScriptAnalyzer -Scope CurrentUser -Force + Install-Module Pester -Scope CurrentUser -Force + + - name: Lint scripts with PSScriptAnalyzer (non-blocking for now) + shell: pwsh + run: | + $settings = ".config/PSScriptAnalyzerSettings.psd1" + if (!(Test-Path $settings)) { $settings = $null } + $target = (Test-Path "scripts") ? "scripts" : "." + $results = Invoke-ScriptAnalyzer -Path $target -Recurse -Settings $settings -ReportSummary -ErrorAction Continue + if ($results) { + "PSScriptAnalyzer findings (non-blocking):" + $results | Format-Table + } else { + "No PSScriptAnalyzer issues found." + } + + - name: Run Pester tests + shell: pwsh + run: | + if (Test-Path "tests") { + Invoke-Pester -Path "tests" -CI -Output Detailed + } else { + "No tests directory found." + } diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml new file mode 100644 index 0000000..608e704 --- /dev/null +++ b/.github/workflows/markdown.yml @@ -0,0 +1,16 @@ +name: Docs Lint (markdownlint) + +on: + pull_request: + paths: ["**/*.md"] + push: + branches: [ "main", "feat/**" ] + paths: ["**/*.md"] + +jobs: + markdownlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: DavidAnson/markdownlint-cli2-action@v17 + # The action will auto-read .markdownlint-cli2.jsonc at repo root diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 0000000..453f36f --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,20 @@ +{ + "config": { + "default": true, + + // Relaxed rules for existing README content + "MD001": false, // heading-increment + "MD009": false, // no-trailing-spaces + "MD012": false, // no-multiple-blanks + "MD022": false, // blanks-around-headings + "MD032": false, // blanks-around-lists + + // Keep these relaxed ones too + "MD013": false, // line length + "MD033": false, // inline HTML + "MD041": false, // first line heading + "MD007": { "indent": 2 } + }, + "globs": ["**/*.md"], + "ignores": ["**/node_modules", "**/.git"] +} diff --git a/README.md b/README.md index 3d151ad..de44c72 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +![CI](https://github.com/Johnholli/vulnerability-management-program/actions/workflows/ci.yml/badge.svg) +![Docs Lint](https://github.com/Johnholli/vulnerability-management-program/actions/workflows/markdown.yml/badge.svg) + # Vulnerability Management Program Implementation In this project, we simulate the implementation of a comprehensive vulnerability management program, from inception to completion. diff --git a/remediation-wireshark-uninstall.ps1 b/scripts/remediation-wireshark-uninstall.ps1 similarity index 100% rename from remediation-wireshark-uninstall.ps1 rename to scripts/remediation-wireshark-uninstall.ps1 diff --git a/toggle-cipher-suites.ps1 b/scripts/toggle-cipher-suites.ps1 similarity index 100% rename from toggle-cipher-suites.ps1 rename to scripts/toggle-cipher-suites.ps1 diff --git a/toggle-guest-local-administrators.ps1 b/scripts/toggle-guest-local-administrators.ps1 similarity index 100% rename from toggle-guest-local-administrators.ps1 rename to scripts/toggle-guest-local-administrators.ps1 diff --git a/toggle-protocols.ps1 b/scripts/toggle-protocols.ps1 similarity index 100% rename from toggle-protocols.ps1 rename to scripts/toggle-protocols.ps1 diff --git a/tests/Scripts.Tests.ps1 b/tests/Scripts.Tests.ps1 new file mode 100644 index 0000000..d144811 --- /dev/null +++ b/tests/Scripts.Tests.ps1 @@ -0,0 +1,40 @@ +# Pester v5 advisory tests — log findings, never fail CI + +Import-Module Pester + +Describe 'Remediation scripts (advisory only)' { + $repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') + $scriptsDir = Join-Path $repoRoot 'scripts' + + if (-not (Test-Path $scriptsDir)) { + It 'scripts directory missing (ok for now)' { $true | Should -BeTrue } + return + } + + $scripts = @(Get-ChildItem -Path $scriptsDir -Filter *.ps1 -File -ErrorAction SilentlyContinue) + + # Always pass; just print info for learning + It 'enumerate scripts (advisory)' { + "Found $($scripts.Count) scripts under /scripts" + $true | Should -BeTrue + } + + foreach ($script in $scripts) { + It "parse check (advisory): $($script.Name)" { + $tokens = $null; $errors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile($script.FullName, [ref]$tokens, [ref]$errors) + if ($errors -and $errors.Count -gt 0) { + Write-Warning "Parser reported $($errors.Count) error(s) in $($script.Name)" + } + $true | Should -BeTrue + } + + It "Write-Host advisory: $($script.Name)" { + $content = Get-Content $script.FullName -Raw + if ($content -match '\bWrite-Host\b') { + Write-Warning "Write-Host found in $($script.Name) — advisory only" + } + $true | Should -BeTrue + } + } +}