# .SYNOPSIS Scans VS Code extensions for security vulnerabilities using npm audit and Snyk CLI. .DESCRIPTION This script lists installed VS Code extensions, downloads their NPM packages, and runs security audits using npm audit and/or Snyk CLI to identify vulnerabilities. .PARAMETER OutputPath Directory where extension packages will be downloaded. Defaults to ./vscode-extensions-audit .PARAMETER UseSnyk Switch to enable Snyk CLI scanning in addition to npm audit .EXAMPLE .\vscodepluginscan.ps1 .EXAMPLE .\vscodepluginscan.ps1 -OutputPath "C:\temp\extensions" -UseSnyk #> [CmdletBinding()] param( [string]$OutputPath = "./vscode-extensions-audit", [switch]$UseSnyk ) # Check if VS Code is installed if (-not (Get-Command code -ErrorAction SilentlyContinue)) { Write-Error "VS Code CLI 'code' not found. Please ensure VS Code is installed and added to PATH." exit 1 } # Check if npm is installed if (-not (Get-Command npm -ErrorAction SilentlyContinue)) { Write-Error "npm not found. Please ensure Node.js and npm are installed." exit 1 } # Check if Snyk is installed when requested if ($UseSnyk -and -not (Get-Command snyk -ErrorAction SilentlyContinue)) { Write-Warning "Snyk CLI not found. Install with: npm install -g snyk" $UseSnyk = $false } # Create output directory if (-not (Test-Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null } Write-Host "Starting VS Code extension security scan..." -ForegroundColor Green Write-Host "Output directory: $OutputPath" -ForegroundColor Cyan # Get list of installed extensions Write-Host "`nGetting list of installed VS Code extensions..." -ForegroundColor Yellow $extensions = & code --list-extensions --show-versions if ($extensions.Count -eq 0) { Write-Warning "No extensions found." exit 0 } Write-Host "Found $($extensions.Count) extensions" -ForegroundColor Green $auditResults = @() foreach ($extension in $extensions) { $extensionParts = $extension -split '@' $extensionName = $extensionParts[0] $extensionVersion = $extensionParts[1] Write-Host "`nProcessing: $extensionName@$extensionVersion" -ForegroundColor Cyan # Get extension installation path $vsCodeExtensionsPath = "" if ($env:USERPROFILE) { $vsCodeExtensionsPath = Join-Path $env:USERPROFILE ".vscode\extensions" } # Find the actual extension directory $extensionInstallDir = Get-ChildItem -Path $vsCodeExtensionsPath -Directory | Where-Object { $_.Name -like "$extensionName-*" } | Select-Object -First 1 if (-not $extensionInstallDir) { Write-Warning " Extension directory not found for $extensionName. Skipping." continue } Write-Host " Found extension at: $($extensionInstallDir.FullName)" -ForegroundColor Gray # Check if extension has package.json with dependencies $extensionPackageJson = Join-Path $extensionInstallDir.FullName "package.json" if (-not (Test-Path $extensionPackageJson)) { Write-Host " No package.json found for $extensionName. Skipping audit." -ForegroundColor Yellow continue } # Create extension-specific directory for audit $extensionDir = Join-Path $OutputPath $extensionName.Replace('.', '-') if (-not (Test-Path $extensionDir)) { New-Item -ItemType Directory -Path $extensionDir -Force | Out-Null } # Copy package.json if it doesn't exist or is different version $targetPackageJson = Join-Path $extensionDir "package.json" $shouldCopyPackageJson = $true if (Test-Path $targetPackageJson) { try { $sourceContent = Get-Content $extensionPackageJson | ConvertFrom-Json $targetContent = Get-Content $targetPackageJson | ConvertFrom-Json if ($sourceContent.version -eq $targetContent.version -and $sourceContent.name -eq $targetContent.name) { $shouldCopyPackageJson = $false Write-Host " Package.json already exists with same version. Skipping copy." -ForegroundColor Gray } } catch { # If we can't compare, copy anyway Write-Host " Could not compare package.json versions. Copying anyway." -ForegroundColor Yellow } } if ($shouldCopyPackageJson) { Copy-Item $extensionPackageJson $extensionDir -Force } # Copy package-lock.json if it exists and package.json was copied $extensionPackageLock = Join-Path $extensionInstallDir.FullName "package-lock.json" if ($shouldCopyPackageJson -and (Test-Path $extensionPackageLock)) { Copy-Item $extensionPackageLock $extensionDir -Force } # Copy node_modules if it exists and package.json was copied (some extensions have pre-installed dependencies) $extensionNodeModules = Join-Path $extensionInstallDir.FullName "node_modules" if ($shouldCopyPackageJson -and (Test-Path $extensionNodeModules)) { Write-Host " Copying existing node_modules..." -ForegroundColor Gray Copy-Item $extensionNodeModules $extensionDir -Recurse -Force } Push-Location $extensionDir try { # Check if there are any dependencies to audit $packageContent = Get-Content "package.json" | ConvertFrom-Json $hasDependencies = $packageContent.dependencies -or $packageContent.devDependencies if (-not $hasDependencies) { Write-Host " No dependencies found in $extensionName. Skipping audit." -ForegroundColor Yellow Pop-Location continue } # Install dependencies if node_modules doesn't exist if (-not (Test-Path "node_modules")) { Write-Host " Installing dependencies..." -ForegroundColor Gray $installOutput = & npm install --production 2>&1 if ($LASTEXITCODE -ne 0) { Write-Warning " Failed to install dependencies for $extensionName. Attempting audit anyway." } } # Run npm audit Write-Host " Running npm audit..." -ForegroundColor Gray $auditOutput = & npm audit --json 2>&1 $auditResult = @{ Extension = $extension NpmAudit = $auditOutput SnykAudit = $null } # Parse npm audit results try { $auditJson = $auditOutput | ConvertFrom-Json -ErrorAction SilentlyContinue if ($auditJson.vulnerabilities) { $vulnCount = ($auditJson.vulnerabilities.PSObject.Properties | Measure-Object).Count if ($vulnCount -gt 0) { Write-Host " ⚠️ Found $vulnCount vulnerabilities in $extensionName" -ForegroundColor Red } else { Write-Host " ✅ No vulnerabilities found in $extensionName" -ForegroundColor Green } } } catch { Write-Host " ℹ️ Audit completed for $extensionName" -ForegroundColor Blue } # Run Snyk if requested and available if ($UseSnyk) { Write-Host " Running Snyk scan..." -ForegroundColor Gray $snykOutput = & snyk test --json 2>&1 $auditResult.SnykAudit = $snykOutput try { $snykJson = $snykOutput | ConvertFrom-Json -ErrorAction SilentlyContinue if ($snykJson.vulnerabilities) { $snykVulnCount = $snykJson.vulnerabilities.Count if ($snykVulnCount -gt 0) { Write-Host " ⚠️ Snyk found $snykVulnCount vulnerabilities in $extensionName" -ForegroundColor Red } else { Write-Host " ✅ Snyk found no vulnerabilities in $extensionName" -ForegroundColor Green } } else { Write-Host " ✅ Snyk found no vulnerabilities in $extensionName" -ForegroundColor Green } } catch { Write-Host " ℹ️ Snyk scan completed for $extensionName" -ForegroundColor Blue } } $auditResults += $auditResult } catch { Write-Error " Error processing $extensionName`: $_" } finally { Pop-Location } } # Generate summary report Write-Host "`n" + "="*60 -ForegroundColor Green Write-Host "AUDIT SUMMARY REPORT" -ForegroundColor Green Write-Host "="*60 -ForegroundColor Green $reportPath = Join-Path $OutputPath "audit-report.json" $auditResults | ConvertTo-Json -Depth 5 | Out-File -FilePath $reportPath -Encoding UTF8 # Generate HTML report $htmlReportPath = Join-Path $OutputPath "audit-report.html" $htmlContent = @"