Skip to main content
Glama

USPTO Final Petition Decisions MCP Server

by john-walkoe
Validation-Helpers.psm1β€’18.1 kB
<# .SYNOPSIS API Key and Input Validation Module for USPTO MCP Deployment (PowerShell) .DESCRIPTION Provides validation functions for USPTO and Mistral API keys, path validation, and secure secret generation for Windows deployment scripts. Security: Prevents deployment with invalid/placeholder keys Part of: Security fixes for deploy scripts (audit findings) .NOTES Date: 2025-11-18 Author: USPTO MCP Security Team Version: 1.0.0 .EXAMPLE Import-Module .\Validation-Helpers.psm1 Test-UsptoApiKey -ApiKey $myKey #> # ============================================ # API Key Format Validation Functions # ============================================ function Test-UsptoApiKey { <# .SYNOPSIS Validates USPTO API key format .DESCRIPTION USPTO API Key Format: - Length: Exactly 30 characters - Characters: Lowercase letters only (a-z) - Example: abcdefghijklmnopqrstuvwxyzabcd .PARAMETER ApiKey The API key to validate .PARAMETER Silent If specified, don't write output (return only) .OUTPUTS Boolean - $true if valid, $false if invalid .EXAMPLE Test-UsptoApiKey -ApiKey "abcdefghijklmnopqrstuvwxyzabcd" #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory=$true)] [string]$ApiKey, [Parameter(Mandatory=$false)] [switch]$Silent ) # Check if empty if ([string]::IsNullOrWhiteSpace($ApiKey)) { if (-not $Silent) { Write-Host "[ERROR] USPTO API key is empty" -ForegroundColor Red } return $false } # Check exact length (30 characters) if ($ApiKey.Length -ne 30) { if (-not $Silent) { Write-Host "[ERROR] USPTO API key must be exactly 30 characters (got $($ApiKey.Length))" -ForegroundColor Red Write-Host " Expected format: 30 lowercase letters (a-z)" -ForegroundColor Yellow } return $false } # Check character set (only lowercase letters) if ($ApiKey -notmatch '^[a-z]{30}$') { if (-not $Silent) { Write-Host "[ERROR] USPTO API key must contain only lowercase letters (a-z)" -ForegroundColor Red Write-Host " Invalid characters detected in key" -ForegroundColor Yellow } return $false } # Check for placeholder patterns if (Test-PlaceholderPattern -Value $ApiKey -KeyType "USPTO" -Silent:$Silent) { return $false } # Check for obvious test patterns if ($ApiKey -match '^(test|demo|sample|example)') { if (-not $Silent) { Write-Host "[WARN] USPTO API key starts with 'test/demo/sample' - is this a real key?" -ForegroundColor Yellow $confirm = Read-Host "Continue anyway? (y/N)" if ($confirm -ne 'y' -and $confirm -ne 'Y') { Write-Host "[INFO] Validation cancelled by user" -ForegroundColor Yellow return $false } } } if (-not $Silent) { Write-Host "[OK] USPTO API key format validated (30 chars, lowercase)" -ForegroundColor Green } return $true } function Test-MistralApiKey { <# .SYNOPSIS Validates Mistral API key format .DESCRIPTION Mistral API Key Format: - Length: Exactly 32 characters - Characters: Letters (a-z, A-Z) and numbers (0-9) - Example: AbCdEfGh1234567890IjKlMnOp1234 .PARAMETER ApiKey The API key to validate (can be empty - Mistral is optional) .PARAMETER Silent If specified, don't write output (return only) .OUTPUTS Boolean - $true if valid, $false if invalid .EXAMPLE Test-MistralApiKey -ApiKey "AbCdEfGh1234567890IjKlMnOp1234" #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory=$false)] [string]$ApiKey, [Parameter(Mandatory=$false)] [switch]$Silent ) # Empty is allowed (Mistral is optional) if ([string]::IsNullOrWhiteSpace($ApiKey)) { if (-not $Silent) { Write-Host "[INFO] Mistral API key is optional - skipping validation" -ForegroundColor Cyan } return $true } # Check exact length (32 characters) if ($ApiKey.Length -ne 32) { if (-not $Silent) { Write-Host "[ERROR] Mistral API key must be exactly 32 characters (got $($ApiKey.Length))" -ForegroundColor Red Write-Host " Expected format: 32 alphanumeric characters (a-z, A-Z, 0-9)" -ForegroundColor Yellow } return $false } # Check character set (letters and numbers only) if ($ApiKey -notmatch '^[a-zA-Z0-9]{32}$') { if (-not $Silent) { Write-Host "[ERROR] Mistral API key must contain only letters (a-z, A-Z) and numbers (0-9)" -ForegroundColor Red Write-Host " Invalid characters detected in key" -ForegroundColor Yellow } return $false } # Check for placeholder patterns if (Test-PlaceholderPattern -Value $ApiKey -KeyType "Mistral" -Silent:$Silent) { return $false } # Check for obvious test patterns if ($ApiKey -match '^(test|demo|sample|example)') { if (-not $Silent) { Write-Host "[WARN] Mistral API key starts with 'test/demo/sample' - is this a real key?" -ForegroundColor Yellow $confirm = Read-Host "Continue anyway? (y/N)" if ($confirm -ne 'y' -and $confirm -ne 'Y') { Write-Host "[INFO] Validation cancelled by user" -ForegroundColor Yellow return $false } } } if (-not $Silent) { Write-Host "[OK] Mistral API key format validated (32 chars, alphanumeric)" -ForegroundColor Green } return $true } function Test-PlaceholderPattern { <# .SYNOPSIS Checks for common placeholder patterns in API keys .DESCRIPTION Detects common placeholder text like "your_api_key_here", "placeholder", etc. .PARAMETER Value The value to check .PARAMETER KeyType The type of key (for error messages) .PARAMETER Silent If specified, don't write output (return only) .OUTPUTS Boolean - $true if placeholder detected (invalid), $false if no placeholder #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory=$true)] [string]$Value, [Parameter(Mandatory=$true)] [string]$KeyType, [Parameter(Mandatory=$false)] [switch]$Silent ) # Common placeholder patterns $placeholderPatterns = @( 'your.*key', 'your.*api', 'api.*key.*here', 'placeholder', 'insert.*key', 'insert.*api', 'replace.*me', 'replace.*key', 'changeme', 'change.*me', 'put.*key.*here', 'add.*key.*here', 'enter.*key', 'paste.*key', 'fill.*in' ) # Check each pattern (case-insensitive) foreach ($pattern in $placeholderPatterns) { if ($Value -match $pattern) { if (-not $Silent) { Write-Host "[ERROR] Detected placeholder pattern in $KeyType API key: '$pattern'" -ForegroundColor Red Write-Host " Please use your actual API key, not a placeholder" -ForegroundColor Yellow } return $true # Placeholder found (error) } } return $false # No placeholder found (success) } # ============================================ # Path Validation Functions # ============================================ function Test-PathSecurity { <# .SYNOPSIS Validates directory path for security .DESCRIPTION Checks for: - Path traversal attempts (..) - Absolute vs relative paths - Sensitive system directories .PARAMETER Path The path to validate .PARAMETER PathName Description of the path (for error messages) .PARAMETER Silent If specified, don't write output (return only) .OUTPUTS Boolean - $true if valid, $false if invalid #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory=$true)] [string]$Path, [Parameter(Mandatory=$true)] [string]$PathName, [Parameter(Mandatory=$false)] [switch]$Silent ) # Check if empty if ([string]::IsNullOrWhiteSpace($Path)) { if (-not $Silent) { Write-Host "[ERROR] $PathName cannot be empty" -ForegroundColor Red } return $false } # Check for path traversal attempts (..) if ($Path -match '\.\.') { if (-not $Silent) { Write-Host "[ERROR] $PathName contains path traversal (..): $Path" -ForegroundColor Red Write-Host " This is a security risk - path rejected" -ForegroundColor Red } return $false } # Check for absolute path (Windows: C:\, D:\, etc.) if (-not ($Path -match '^[A-Za-z]:\\' -or $Path -match '^\\\\')) { if (-not $Silent) { Write-Host "[WARN] $PathName should be an absolute path" -ForegroundColor Yellow Write-Host " Got: $Path" -ForegroundColor Yellow $confirm = Read-Host "Continue anyway? (y/N)" if ($confirm -ne 'y' -and $confirm -ne 'Y') { Write-Host "[INFO] Path validation rejected by user" -ForegroundColor Yellow return $false } } } # Warn about Windows system directories $systemDirs = @('C:\Windows', 'C:\Program Files', 'C:\Program Files (x86)', 'C:\ProgramData') foreach ($sysDir in $systemDirs) { if ($Path -like "$sysDir*") { if (-not $Silent) { Write-Host "[WARN] $PathName targets system directory: $Path" -ForegroundColor Yellow Write-Host " This could require administrator privileges" -ForegroundColor Yellow } break } } if (-not $Silent) { Write-Host "[OK] $PathName validated: $Path" -ForegroundColor Green } return $true } # ============================================ # Secret Generation Functions # ============================================ function New-SecureSecret { <# .SYNOPSIS Generates a cryptographically secure random secret .DESCRIPTION Uses .NET's RNGCryptoServiceProvider for secure random generation Returns base64-encoded secret .PARAMETER Length Length in bytes (default 32) .OUTPUTS String - Base64-encoded secure random secret .EXAMPLE $secret = New-SecureSecret -Length 32 #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory=$false)] [int]$Length = 32 ) try { $bytes = New-Object byte[] $Length $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() $rng.GetBytes($bytes) $rng.Dispose() $secret = [System.Convert]::ToBase64String($bytes) return $secret } catch { Write-Host "[ERROR] Failed to generate secure secret: $_" -ForegroundColor Red return $null } } # ============================================ # User Input Functions # ============================================ function Read-ApiKeySecure { <# .SYNOPSIS Prompts user for API key with hidden input .DESCRIPTION Reads API key input with masking (not visible on screen) .PARAMETER Prompt The prompt text to display .OUTPUTS String - The entered API key .EXAMPLE $key = Read-ApiKeySecure -Prompt "Enter your USPTO API key" #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory=$true)] [string]$Prompt ) $secureString = Read-Host -Prompt $Prompt -AsSecureString $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString) $apiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) return $apiKey } function Read-UsptoApiKeyWithValidation { <# .SYNOPSIS Prompts for USPTO API key with validation loop .DESCRIPTION Repeatedly prompts user until a valid USPTO API key is entered .PARAMETER MaxAttempts Maximum number of attempts (default 3) .OUTPUTS String - Valid USPTO API key, or $null if failed .EXAMPLE $key = Read-UsptoApiKeyWithValidation #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory=$false)] [int]$MaxAttempts = 3 ) $attempt = 0 while ($attempt -lt $MaxAttempts) { $attempt++ $key = Read-ApiKeySecure -Prompt "Enter your USPTO API key" if ([string]::IsNullOrWhiteSpace($key)) { Write-Host "[ERROR] USPTO API key cannot be empty" -ForegroundColor Red if ($attempt -lt $MaxAttempts) { Write-Host "[INFO] Attempt $attempt of $MaxAttempts" -ForegroundColor Yellow } continue } if (Test-UsptoApiKey -ApiKey $key) { return $key } else { if ($attempt -lt $MaxAttempts) { Write-Host "[WARN] Attempt $attempt of $MaxAttempts - please try again" -ForegroundColor Yellow Write-Host "[INFO] USPTO API key format: 30 lowercase letters (a-z)" -ForegroundColor Cyan } } } Write-Host "[ERROR] Failed to provide valid USPTO API key after $MaxAttempts attempts" -ForegroundColor Red return $null } function Read-MistralApiKeyWithValidation { <# .SYNOPSIS Prompts for Mistral API key with validation loop (optional) .DESCRIPTION Repeatedly prompts user until a valid Mistral API key is entered or skipped .PARAMETER MaxAttempts Maximum number of attempts (default 3) .OUTPUTS String - Valid Mistral API key, empty string if skipped, or $null if failed .EXAMPLE $key = Read-MistralApiKeyWithValidation #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory=$false)] [int]$MaxAttempts = 3 ) Write-Host "[INFO] Mistral API key is OPTIONAL (for OCR on scanned documents)" -ForegroundColor Cyan Write-Host "[INFO] Press Enter to skip, or enter your 32-character Mistral API key" -ForegroundColor Cyan Write-Host "" $attempt = 0 while ($attempt -lt $MaxAttempts) { $attempt++ $key = Read-ApiKeySecure -Prompt "Enter your Mistral API key (or press Enter to skip)" # Empty is OK (optional) if ([string]::IsNullOrWhiteSpace($key)) { Write-Host "[INFO] Skipping Mistral API key (OCR disabled)" -ForegroundColor Yellow return "" } if (Test-MistralApiKey -ApiKey $key) { return $key } else { if ($attempt -lt $MaxAttempts) { Write-Host "[WARN] Attempt $attempt of $MaxAttempts - please try again" -ForegroundColor Yellow Write-Host "[INFO] Mistral API key format: 32 alphanumeric characters (a-z, A-Z, 0-9)" -ForegroundColor Cyan } } } Write-Host "[ERROR] Failed to provide valid Mistral API key after $MaxAttempts attempts" -ForegroundColor Red return $null } # ============================================ # Utility Functions # ============================================ function Show-ApiKeyRequirements { <# .SYNOPSIS Displays API key format requirements .DESCRIPTION Shows formatted information about USPTO and Mistral API key requirements #> [CmdletBinding()] param() Write-Host "" Write-Host "API Key Format Requirements:" -ForegroundColor Cyan Write-Host "============================" -ForegroundColor Cyan Write-Host "" Write-Host "USPTO API Key:" -ForegroundColor White Write-Host " - Required: YES" -ForegroundColor Green Write-Host " - Length: Exactly 30 characters" Write-Host " - Format: Lowercase letters only (a-z)" Write-Host " - Example: abcdefghijklmnopqrstuvwxyzabcd" Write-Host " - Get from: https://data.uspto.gov/myodp/" -ForegroundColor Yellow Write-Host "" Write-Host "Mistral API Key:" -ForegroundColor White Write-Host " - Required: NO (optional, for OCR)" -ForegroundColor Yellow Write-Host " - Length: Exactly 32 characters" Write-Host " - Format: Letters (a-z, A-Z) and numbers (0-9)" Write-Host " - Example: AbCdEfGh1234567890IjKlMnOp1234" Write-Host " - Get from: https://console.mistral.ai/" -ForegroundColor Yellow Write-Host "" } function Hide-ApiKey { <# .SYNOPSIS Masks API key for display (shows last 5 characters only) .PARAMETER ApiKey The API key to mask .PARAMETER VisibleChars Number of characters to show at end (default 5) .OUTPUTS String - Masked API key .EXAMPLE Hide-ApiKey -ApiKey "abcdefghijklmnopqrstuvwxyzabcd" # Returns: *************************abcd #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory=$true)] [string]$ApiKey, [Parameter(Mandatory=$false)] [int]$VisibleChars = 5 ) if ([string]::IsNullOrWhiteSpace($ApiKey)) { return "[Not set]" } elseif ($ApiKey.Length -le $VisibleChars) { return "***" } else { $maskedLength = $ApiKey.Length - $VisibleChars $asterisks = '*' * $maskedLength $visible = $ApiKey.Substring($ApiKey.Length - $VisibleChars) return "$asterisks$visible" } } # ============================================ # Export Module Members # ============================================ Export-ModuleMember -Function @( 'Test-UsptoApiKey', 'Test-MistralApiKey', 'Test-PlaceholderPattern', 'Test-PathSecurity', 'New-SecureSecret', 'Read-ApiKeySecure', 'Read-UsptoApiKeyWithValidation', 'Read-MistralApiKeyWithValidation', 'Show-ApiKeyRequirements', 'Hide-ApiKey' )

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/john-walkoe/uspto_fpd_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server