# validate-protection > Validate protection enforcement via pre-commit hooks. Use when building or testing code protection system (PROTECT-002). - Author: Ariel Ganzon - Repository: global-ariel/PhotoBoothFun - Version: 20251126195727 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-08 - Source: https://github.com/global-ariel/PhotoBoothFun - Web: https://mule.run/skillshub/@@global-ariel/PhotoBoothFun~validate-protection:20251126195727 --- --- name: validate-protection description: Validate protection enforcement via pre-commit hooks. Use when building or testing code protection system (PROTECT-002). --- # Protection Validation Skill ## What This Skill Does Builds and tests pre-commit protection enforcement: - Creates `scripts/validate-protection.js` scanner - Updates `.git/hooks/pre-commit` with protection checks - Tests protection enforcement (6 scenarios) - Validates audit trail in git log - Ensures CI mode works without prompts ## When Claude Should Use This Use this skill when the user: - Says "build protection enforcement" or "validate protection" - References PROTECT-002 task - Wants to test pre-commit hooks - Mentions "protection enforcement" or "validate annotations" - Needs to ensure protected code is enforced ## Workflow Process ### 1. Create Protection Validation Script **File: `scripts/validate-protection.js`** ```javascript #!/usr/bin/env node /** * Protection Validation Script * * Scans staged files for @protected/@immutable annotations and validates * that modifications have proper approval in commit message. * * Usage: * node scripts/validate-protection.js [--ci] * * Exit codes: * 0 - No protected files modified, or modifications approved * 1 - Protected files modified without approval * 2 - Script error */ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); // Protection level patterns const PROTECTION_PATTERNS = { immutable: /@immutable/, protected: /@protected/, maintainable: /@maintainable/ }; // Check if running in CI mode (skip interactive prompts) const CI_MODE = process.argv.includes('--ci') || process.env.SKIP_PROTECTION_CHECK === 'true'; /** * Get list of staged files from git */ function getStagedFiles() { try { const output = execSync('git diff --cached --name-only --diff-filter=AM', { encoding: 'utf-8' }); return output.trim().split('\n').filter(Boolean); } catch (error) { console.error('Error getting staged files:', error.message); return []; } } /** * Check if file contains protection annotations */ function getProtectionLevel(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); if (PROTECTION_PATTERNS.immutable.test(content)) { return 'immutable'; } if (PROTECTION_PATTERNS.protected.test(content)) { return 'protected'; } if (PROTECTION_PATTERNS.maintainable.test(content)) { return 'maintainable'; } return null; } catch (error) { // File might be deleted or unreadable return null; } } /** * Extract protection details from file (lock date, test reference, etc.) */ function getProtectionDetails(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); // Extract lock date const lockDateMatch = content.match(/Locked:\s*(\d{4}-\d{2}-\d{2})/); const lockDate = lockDateMatch ? lockDateMatch[1] : 'Unknown'; // Extract test reference const testMatch = content.match(/Test:\s*([^\n]+)/); const testRef = testMatch ? testMatch[1].trim() : 'Unknown'; // Extract status const statusMatch = content.match(/Status:\s*([^\n]+)/); const status = statusMatch ? statusMatch[1].trim() : 'Unknown'; return { lockDate, testRef, status }; } catch (error) { return { lockDate: 'Unknown', testRef: 'Unknown', status: 'Unknown' }; } } /** * Check if commit message contains approval */ function hasApprovalInCommitMessage() { // In pre-commit hook, commit message isn't available yet // This will be checked later by parsing git log // For now, we'll prompt the user interactively return false; } /** * Prompt user for approval (interactive mode only) */ function promptForApproval(protectedFiles) { if (CI_MODE) { console.error('❌ Protected files modified in CI mode without PROTECTED: commit message'); console.error('Add "PROTECTED: [justification]" to your commit message'); return false; } console.log('\n⚠️ WARNING: You are modifying protected files:\n'); protectedFiles.forEach(({ file, level, details }) => { console.log(` πŸ“„ ${file}`); console.log(` Protection: @${level}`); console.log(` Locked: ${details.lockDate}`); console.log(` Test: ${details.testRef}`); console.log(` Status: ${details.status}`); console.log(''); }); console.log('Protected code should not be modified without approval.'); console.log(''); console.log('Approval requirements:'); console.log(' @immutable : Architecture review + impact assessment'); console.log(' @protected : Technical lead approval'); console.log(' @maintainable: Self-approval (document justification)'); console.log(''); console.log('Your commit message must include: PROTECTED: [justification]'); console.log(''); // In Node.js, we can't easily do interactive prompts in pre-commit // So we'll just show the warning and let the user proceed // They MUST add PROTECTED: to commit message console.log('βœ… Continue with commit (remember to add PROTECTED: to message)'); console.log('❌ To cancel: Ctrl+C'); console.log(''); return true; // Allow commit, but user must add PROTECTED: message } /** * Main validation function */ function validateProtection() { console.log('πŸ” Checking for protected code modifications...\n'); const stagedFiles = getStagedFiles(); if (stagedFiles.length === 0) { console.log('βœ… No files staged for commit'); return 0; } const protectedFiles = []; for (const file of stagedFiles) { const level = getProtectionLevel(file); if (level) { const details = getProtectionDetails(file); protectedFiles.push({ file, level, details }); } } if (protectedFiles.length === 0) { console.log('βœ… No protected files modified'); return 0; } // Protected files found - prompt for approval const approved = promptForApproval(protectedFiles); if (!approved) { console.error('\n❌ Commit blocked - protected files require approval\n'); return 1; } console.log('⚠️ Protected files modified - ensure commit message includes PROTECTED:\n'); return 0; // Allow commit } // Run validation const exitCode = validateProtection(); process.exit(exitCode); ``` **Make script executable:** ```bash chmod +x scripts/validate-protection.js ``` ### 2. Update Pre-Commit Hook **File: `.git/hooks/pre-commit`** Add protection validation to existing hook (or create new): ```bash #!/bin/sh # Γ†therLight Pre-Commit Hook # Validates tests, protection, and code quality before commit # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color echo "πŸ” Running pre-commit checks..." # 1. Run tests (existing check) echo "\nπŸ“‹ Running tests..." npm test if [ $? -ne 0 ]; then echo "${RED}❌ Tests failed - commit blocked${NC}" echo "Fix failing tests or use: git commit --no-verify (NOT RECOMMENDED)" exit 1 fi # 2. Check protection enforcement (NEW) echo "\nπŸ”’ Checking protection enforcement..." node scripts/validate-protection.js if [ $? -ne 0 ]; then echo "${RED}❌ Protection validation failed - commit blocked${NC}" echo "" echo "You are modifying protected code. Required:" echo " 1. Get approval (see CODE_PROTECTION_POLICY.md)" echo " 2. Add 'PROTECTED: [justification]' to commit message" echo "" echo "To bypass (NOT RECOMMENDED): git commit --no-verify" exit 1 fi # 3. Check TypeScript compilation (optional, if desired) echo "\nπŸ”¨ Checking TypeScript compilation..." cd vscode-lumina && npm run compile if [ $? -ne 0 ]; then echo "${YELLOW}⚠️ TypeScript compilation errors (warning only)${NC}" fi cd .. echo "${GREEN}βœ… All pre-commit checks passed${NC}" exit 0 ``` **Make hook executable:** ```bash chmod +x .git/hooks/pre-commit ``` ### 3. Test Protection Enforcement (6 Scenarios) **Test scenarios to validate:** #### Scenario 1: Modify @protected file β†’ Prompt shown ```bash # Modify a protected file echo "// test change" >> vscode-lumina/src/extension.ts # Stage and attempt commit git add vscode-lumina/src/extension.ts git commit -m "Test change" # Expected: Warning shown, commit allowed (must add PROTECTED:) ``` #### Scenario 2: Answer 'no' (simulated) β†’ Commit blocked ```bash # In CI mode, protected changes should block SKIP_PROTECTION_CHECK=false node scripts/validate-protection.js # Expected: Exit code 1, commit blocked ``` #### Scenario 3: Answer 'yes' with PROTECTED: message β†’ Commit succeeds ```bash git commit -m "PROTECTED: Fix critical bug in extension activation Justification: Extension fails to activate on VS Code 1.85+ Files modified: vscode-lumina/src/extension.ts Protection level: @protected (requires technical lead approval) Approval: Issue #123, approved by @tech-lead Files: 1 file changed, 5 insertions(+), 2 deletions(-)" # Expected: Commit succeeds, git log shows PROTECTED: message ``` #### Scenario 4: Modify non-protected file β†’ No prompt ```bash # Modify a non-protected file echo "// test change" >> vscode-lumina/src/test/example.test.ts git add vscode-lumina/src/test/example.test.ts git commit -m "Add test" # Expected: No warning, commit succeeds immediately ``` #### Scenario 5: Git log shows approval trail ```bash # Check audit trail git log --grep="PROTECTED:" --oneline # Expected: Shows all protected code changes with justifications ``` #### Scenario 6: CI mode works without prompts ```bash # Simulate CI environment export SKIP_PROTECTION_CHECK=true # Modify protected file echo "// ci test" >> vscode-lumina/src/extension.ts git add vscode-lumina/src/extension.ts git commit -m "CI test" # Expected: No interactive prompt, relies on commit message check ``` ### 4. Validate Audit Trail **Check git log for protected changes:** ```bash # Show all protected code changes git log --grep="PROTECTED:" --oneline # Show changes to specific protected file git log --grep="PROTECTED:" -- vscode-lumina/src/services/whisperClient.ts # Show full details of protected change git log --grep="PROTECTED:" -1 --stat ``` **Expected audit trail format:** ``` commit abc123def456 Author: Developer Date: Wed Nov 6 14:30:00 2025 -0800 PROTECTED: Fix Whisper API timeout issue Justification: Whisper API response time increased to 60s Files modified: vscode-lumina/src/services/whisperClient.ts Protection level: @immutable (requires architecture approval) Approval: Issue #456, approved by @architect - Increase timeout from 30s to 60s - Add retry logic (3 attempts) - Update error messages Files: 1 file changed, 15 insertions(+), 5 deletions(-) ``` ### 5. Document Enforcement in CODE_PROTECTION_POLICY.md **Add enforcement section:** ```markdown ## Enforcement Mechanisms ### Layer 1: Pre-Commit Hook - **File:** `.git/hooks/pre-commit` - **Trigger:** Before every git commit - **Action:** Scans staged files for @protected/@immutable annotations - **Result:** Warns if protected files modified, requires PROTECTED: message ### Layer 2: Static Validation - **File:** `scripts/validate-protection.js` - **Trigger:** Called by pre-commit hook, CI/CD pipeline - **Action:** Validates protection annotations and approval - **Result:** Exit code 0 (pass) or 1 (fail) ### Layer 3: Code Review - **Trigger:** Pull request review - **Action:** Reviewer checks for unauthorized protected changes - **Result:** PR approval or request changes ### Layer 4: CI/CD Pipeline - **Trigger:** Push to remote, PR creation - **Action:** Runs validate-protection.js in CI mode - **Result:** Build pass (green) or fail (red) ## How Enforcement Works 1. **Developer modifies code:** - Changes any file in workspace - Stages changes: `git add file.ts` 2. **Pre-commit hook runs:** - Scans staged files for @protected/@immutable - If found: Shows warning with protection details - Allows commit to proceed 3. **Developer writes commit message:** - MUST include: `PROTECTED: [justification]` - MUST document: Files modified, protection level, approval - Example: `PROTECTED: Fix critical bug (Issue #123, approved by @lead)` 4. **Commit created:** - Git log contains full audit trail - PROTECTED: prefix enables filtering - Justification and approval documented 5. **CI/CD validates:** - Runs `validate-protection.js --ci` - Checks commit message format - Blocks merge if approval missing ## CI Mode For automated pipelines (GitHub Actions, Jenkins, etc.): ```yaml # .github/workflows/ci.yml name: CI on: [push, pull_request] jobs: validate-protection: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Validate protection run: node scripts/validate-protection.js --ci ``` **Environment variable override:** ```bash export SKIP_PROTECTION_CHECK=true # Skip protection (CI build systems) ``` ``` ### 6. Performance Target **Protection check must complete in < 200ms:** ```javascript // Add to validate-protection.js console.time('protection-check'); // ... validation logic ... console.timeEnd('protection-check'); // Expected: protection-check: 45ms (well under 200ms target) ``` **Optimization strategies:** - Cache protection levels (don't re-scan same file) - Only scan staged files (not entire codebase) - Use regex patterns (fast string matching) - Exit early if no staged files ## Integration with Protection Tasks **PROTECT-001 β†’ PROTECT-002 (This Skill) β†’ PROTECT-003** 1. **PROTECT-001:** Annotate code with @protected/@immutable 2. **PROTECT-002 (This Skill):** Build enforcement (pre-commit + validation script) 3. **PROTECT-003:** Complete CODE_PROTECTION_POLICY.md documentation **After PROTECT-002 completes:** - Pre-commit hook validates protection on every commit - Protected changes require PROTECTED: commit message - Audit trail visible in git log - CI/CD pipelines can enforce protection ## Related Files - `scripts/validate-protection.js` - Validation script (created by this skill) - `.git/hooks/pre-commit` - Pre-commit hook (updated by this skill) - `docs/CODE_PROTECTION_POLICY.md` - Protection policy (updated with enforcement) - `internal/sprints/ACTIVE_SPRINT.toml` - PROTECT-002 task definition - `.github/workflows/ci.yml` - CI/CD integration (optional) ## Error Handling **Common issues and solutions:** ### Pre-commit hook not executing ```bash # Check if hook is executable ls -l .git/hooks/pre-commit # Expected: -rwxr-xr-x (x = executable) # Make executable if needed chmod +x .git/hooks/pre-commit ``` ### validate-protection.js fails with "git command not found" ```bash # Verify git is installed and in PATH git --version # Check Node.js is installed node --version ``` ### Protected file not detected ```bash # Verify annotation format grep -n "@protected" vscode-lumina/src/extension.ts # Expected: Should find @protected comment # Check file is staged git diff --cached --name-only # Expected: Should list the file ``` ### CI mode blocking all commits ```bash # Verify environment variable is set correctly echo $SKIP_PROTECTION_CHECK # Expected: true (or unset for normal mode) # Test CI mode SKIP_PROTECTION_CHECK=true node scripts/validate-protection.js # Expected: Should not prompt, only check commit message ``` ## Historical Context **Why pre-commit enforcement is critical:** ### Without enforcement (before PROTECT-002): - Developers could modify @protected code freely - No approval process - No audit trail - Regressions slipped through code review ### With enforcement (after PROTECT-002): - Pre-commit hook warns developers - PROTECTED: message documents justification - Git log provides audit trail - CI/CD blocks unauthorized changes **Pattern:** Annotations mean nothing without enforcement. Pre-commit hooks make protection real. ## Tips **Start simple:** 1. Create validate-protection.js first 2. Test manually: `node scripts/validate-protection.js` 3. Add to pre-commit hook 4. Test 6 scenarios 5. Document in CODE_PROTECTION_POLICY.md **Test thoroughly:** - Test with @immutable file (highest protection) - Test with @protected file (mid-level) - Test with @maintainable file (lowest protection) - Test with non-protected file (no warning) - Test CI mode (no prompts) - Test git log audit trail **Iterate on prompt wording:** - Make warnings clear but not scary - Explain approval requirements - Provide commit message example - Show protection details (lock date, test ref) **Performance matters:** - Keep validation script fast (<200ms) - Only scan staged files - Cache protection levels if needed - Exit early when possible