ESLint Protection: Protect Code from Unexpected Changes

June 9, 2025

ESLint Protection: Protect Code from Unexpected Changes

TL;DR

ESLint Protection is a powerful tool that guards critical code sections from accidental modifications by both humans and automated systems. Using hash-based verification with simple comment annotations, it creates an early warning system that catches unauthorized changes before they reach production. Perfect for protecting security-critical functions, configuration sections, and delicate algorithms that require explicit approval for any modifications.


In modern software development, certain parts of your codebase are more critical than others. Security functions, payment processing logic, configuration sections, and delicate algorithms require extra care when making changes. But how do you protect these sensitive code sections from accidental modifications while still allowing intentional updates when needed?

Enter ESLint Protection - a novel approach to code safety that uses cryptographic hashes and ESLint rules to create a protective barrier around your most important code.

The Problem: Unconscious Code Changes

Every developer has experienced it: you’re refactoring a large codebase, and accidentally modify a critical function. Maybe it’s a security check, a payment calculation, or a configuration that affects the entire application. These changes often slip through code review, especially in large pull requests or when using automated tools.

With the rise of AI-powered development tools like GitHub Copilot, Cursor, and ChatGPT, this problem has become even more pronounced. AI assistants can generate massive, cross-cutting changes that touch dozens of files simultaneously. While these tools are incredibly powerful for productivity, they can inadvertently modify critical code sections without explicitly highlighting the change to developers. A simple request like “refactor this codebase to use async/await” might result in modifications to authentication logic, payment processing, or security validations buried among hundreds of other changes.

Human reviewers, faced with large AI-generated pull requests, often focus on the overall structure and miss critical modifications to protected code sections. The sheer volume of changes can make it impossible to carefully scrutinize every line, especially when the AI helpfully reformats and “improves” code that was intentionally written in a specific way for security or business-critical reasons.

Traditional solutions like code comments or documentation are easily ignored by both humans and AI tools. Git hooks can be bypassed. What we need is something that integrates seamlessly into the development workflow and provides immediate feedback when protected code is modified - whether by human hands or AI assistance.

Consider these real-world scenarios:

Scenario 1: AI Refactoring Gone Wrong

// Original code
function validatePayment(amount) {
  return amount > 0 && amount < 10000; // Critical business rule
}

// After AI "improvement" - business rule accidentally changed
function validatePayment(amount) {
  return amount > 0 && amount <= 10000; // Boundary condition changed!
}

Scenario 2: Cross-cutting Security Changes An AI tool asked to “modernize error handling” might modify exception handling in security-critical functions, accidentally exposing sensitive information or changing authentication flows across multiple files.

Scenario 3: Configuration Drift AI assistants updating configuration files might change critical settings like API endpoints, timeout values, or feature flags without the developer realizing the security or business implications.

How ESLint Protection Works

ESLint Protection introduces a simple but powerful concept: hash-protected code comments. By adding a special comment above critical code sections, you create a cryptographic fingerprint that must match the actual code below it.

Here’s the basic syntax:

//@protected [lines] [hash]

Where:

  • lines is the number of lines to protect (or * for the entire file)
  • hash is a cryptographic hash of the protected code

Getting Started

Installation

First, install the ESLint plugin:

npm add --save-dev @crabnebula/eslint-protection
# or
yarn add -D @crabnebula/eslint-protection
# or
pnpm add -D @crabnebula/eslint-protection

Configuration

Add the plugin to your ESLint configuration:

import { defineConfig } from "eslint/config";
import protection from "@crabnebula/eslint-protection";

export default defineConfig({
  plugins: { protection },
  rules: {
    "protection/protect": "error"
  }
});

Practical Examples

Protecting a Security Function

// Critical authentication logic - requires explicit approval for changes
//@protected 8 a1b2c3d4e5f6789...
function validateUserPermissions(user, resource) {
  if (!user || !user.roles) {
    return false;
  }
  
  const requiredRole = getRequiredRole(resource);
  return user.roles.includes(requiredRole) && user.isActive;
}

Protecting Configuration Sections

//@protected 6 x9y8z7w6v5u4t3s...
const PAYMENT_CONFIG = {
  apiEndpoint: 'https://api.payments.com/v2',
  timeout: 30000,
  retryAttempts: 3,
  currency: 'USD'
};

Protecting an Entire File

//@protected * f7e6d5c4b3a2918...
// This entire configuration file is protected
export const DATABASE_CONFIG = {
  production: {
    host: 'prod-db.company.com',
    port: 5432,
    ssl: true,
    maxConnections: 100
  },
  // ... rest of critical database configuration
};

Generating Protection Hashes

The beauty of ESLint Protection is its simplicity. To protect a section of code:

  1. Add the comment without a hash: //@protected 3
  2. Run ESLint - it will report an error with the correct hash
  3. Copy the hash from the error message back to your comment

For example:

$ npx eslint src/auth.js

/src/auth.js
  12:1  error  Protected code hash mismatch. Expected: a1b2c3d4e5f6789  protection/protect

 1 problem (1 error, 0 warnings)

Making Intentional Changes to Protected Code

When you need to legitimately modify protected code, ESLint Protection provides a clear workflow that ensures changes are deliberate and tracked. Here’s how to properly update protected code sections:

Step 1: Understand What You’re Changing

Before modifying protected code, always review why it was protected in the first place:

//@protected 4 x1y2z3w4v5u6t7s...
function calculateInterestRate(principal, creditScore) {
  // This function implements regulatory compliance requirements
  // Any changes must be approved by legal and compliance teams
  return creditScore > 750 ? 0.045 : 0.065;
}

Step 2: Make Your Changes

Modify the code as needed for your feature or bug fix:

//@protected 5 x1y2z3w4v5u6t7s...  // This hash is now incorrect
function calculateInterestRate(principal, creditScore, accountType) {
  // Added accountType parameter for premium account support
  const baseRate = creditScore > 750 ? 0.045 : 0.065;
  return accountType === 'premium' ? baseRate * 0.9 : baseRate;
}

Step 3: Update the Protection Hash

Run ESLint to get the new hash for your modified code:

$ npx eslint src/financial.js

/src/financial.js
  12:1  error  Protected code hash mismatch. Expected: a9b8c7d6e5f4321  protection/protect

 1 problem (1 error, 0 warnings)

Update the comment with the new hash:

//@protected 5 a9b8c7d6e5f4321...  // Updated hash and line count
function calculateInterestRate(principal, creditScore, accountType) {
  // Added accountType parameter for premium account support
  const baseRate = creditScore > 750 ? 0.045 : 0.065;
  return accountType === 'premium' ? baseRate * 0.9 : baseRate;
}

Step 4: Document the Change

When modifying protected code, always document the reasoning (and protect it too!):

//@protected 6 a9b8c7d6e5f4321...
// Updated 2025-06-29: Added premium account support (Ticket: FEAT-1234)
// Previous hash: x1y2z3w4v5u6t7s... - Legal approval: LA-2025-456
function calculateInterestRate(principal, creditScore, accountType) {
  const baseRate = creditScore > 750 ? 0.045 : 0.065;
  return accountType === 'premium' ? baseRate * 0.9 : baseRate;
}

Temporary Protection Removal

For larger refactoring sessions, you might need to temporarily remove protection:

// Temporarily removing protection for major refactor - Ticket: REFACTOR-789
// Original protection: //@protected 8 z9y8x7w6v5u4t3s...
// TODO: Re-add protection after refactor completion

function complexSecurityFunction(user, resource, context) {
  // Major refactoring in progress...
  // This function will be re-protected once changes are complete
}

Important: Always re-add protection before merging:

//@protected 12 n3w9h4sh1h3r3...
// Re-protected after refactor completion - Ticket: REFACTOR-789  
// Security review completed: SR-2025-123
function complexSecurityFunction(user, resource, context) {
  // Refactored implementation with improved security
}

Bulk Updates with VS Code/Cursor

When making changes to multiple protected sections:

  1. Make all your code changes first
  2. Run ESLint to see all hash mismatches
  3. Use find-and-replace to update multiple hashes at once
# Get all protection errors at once
npx eslint . --format json > protection-errors.json

# Use a script to batch update (example in Node.js)
node scripts/update-protection-hashes.js

Team Workflow for Protected Changes

For team environments, establish a clear process:

Protected Code Change Process

  1. Create a dedicated branch: feature/protected-auth-update
  2. Document the change reason: Link to tickets, requirements, or security reviews
  3. Make minimal changes: Only modify what’s absolutely necessary
  4. Update protection hashes: Run ESLint and update all affected hashes
  5. Request additional review: Tag security/compliance team members
  6. Verify in staging: Test protected code changes thoroughly
  7. Update documentation: Record the change in your security change log

Emergency Scenarios

For urgent production fixes:

// EMERGENCY FIX: Critical security vulnerability - CVE-2025-XXXX
// Original protection temporarily disabled for immediate patch
// //@protected 3 x1y2z3w4v5u6t7s...

function validateAuth(token) {
  // URGENT: Added additional validation to prevent token bypass
  if (!token || token.length < 10) return false;
  return verifyTokenSignature(token) && !isTokenExpired(token);
}

// TODO: Security review and re-protection required within 24 hours
// Tracking: SECURITY-INCIDENT-2025-001

Automation Helpers

Create helper scripts for common protection tasks:

// scripts/update-protection.js
const { execSync } = require('child_process');
const fs = require('fs');

function updateProtectionHashes() {
  try {
    execSync('npx eslint . --format json --output-file lint-results.json');
  } catch (error) {
    // ESLint exits with error code when violations are found
  }
  
  const results = JSON.parse(fs.readFileSync('lint-results.json'));
  const protectionErrors = results.flatMap(file => 
    file.messages
      .filter(msg => msg.ruleId === 'protection/protect')
      .map(msg => ({
        file: file.filePath,
        line: msg.line,
        expectedHash: msg.message.match(/Expected: (\w+)/)[1]
      }))
  );
  
  console.log(`Found ${protectionErrors.length} protection hash mismatches:`);
  protectionErrors.forEach(error => {
    console.log(`${error.file}:${error.line} - New hash: ${error.expectedHash}`);
  });
}

Advanced Setup: Git Hooks Integration

For maximum protection, integrate ESLint Protection with your Git workflow using Husky:

# Install dependencies
npm add --save-dev husky lint-staged @crabnebula/husky-protection

# Initialize Husky
npx husky-init && npm install

# Add pre-commit hooks
npx husky add .husky/pre-commit "npx lint-staged"
npx husky add .husky/pre-commit "node_modules/@crabnebula/husky-protection/index.js"

# Make executable
chmod +x .husky/pre-commit .husky/_/husky.sh

This setup provides two layers of protection:

  1. ESLint Protection: Catches hash mismatches during linting
  2. Husky Protection: Blocks commits that remove more protection comments than they add

The @crabnebula/husky-protection package specifically monitors your Git commits and will fail any commit that removes more protection comments than it adds, ensuring that your protective barriers aren’t accidentally stripped away during refactoring or cleanup operations.

CI/CD Integration with GitHub Actions

Integrating ESLint Protection into your CI/CD pipeline ensures that protected code violations are caught before they reach production. Here are several strategies for implementing this with GitHub Actions.

Basic ESLint Protection Check

name: Code Protection Check
on:
  pull_request:
    branches: [ main, develop ]
  push:
    branches: [ main, develop ]

jobs:
  protection-check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint Protection Check
        run: npx eslint . --ext .js,.ts,.jsx,.tsx
        env:
          # Ensure ESLint exits with error code on protection violations
          NODE_OPTIONS: '--max-old-space-size=4096'

Advanced Protection Workflow

name: Advanced Protection Check
on:
  pull_request:
    branches: [ main ]

jobs:
  protection-audit:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          # Fetch full history to compare protection comments
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Check for removed protection comments
        run: |
          # Count protection comments in main vs current branch
          MAIN_PROTECTIONS=$(git show origin/main -- . | grep -c "//@protected" || echo "0")
          CURRENT_PROTECTIONS=$(grep -r "//@protected" . | wc -l || echo "0")
          
          echo "Protection comments in main: $MAIN_PROTECTIONS"
          echo "Protection comments in current: $CURRENT_PROTECTIONS"
          
          if [ "$CURRENT_PROTECTIONS" -lt "$MAIN_PROTECTIONS" ]; then
            echo "❌ Protection comments have been removed! This requires manual review."
            exit 1
          fi

      - name: Run ESLint with protection rules
        run: npx eslint . --ext .js,.ts,.jsx,.tsx --format json --output-file eslint-results.json
        continue-on-error: true

      - name: Parse and report protection violations
        run: |
          node -e "
          const results = require('./eslint-results.json');
          const protectionErrors = results.flatMap(file => 
            file.messages.filter(msg => msg.ruleId === 'protection/protect')
          );
          
          if (protectionErrors.length > 0) {
            console.log('🛡️ Protected Code Violations Found:');
            protectionErrors.forEach(error => {
              console.log(\`❌ \${error.message}\`);
            });
            process.exit(1);
          } else {
            console.log('✅ All protected code integrity checks passed!');
          }
          "

      - name: Comment on PR with protection status
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const results = JSON.parse(fs.readFileSync('eslint-results.json', 'utf8'));
            const protectionErrors = results.flatMap(file => 
              file.messages.filter(msg => msg.ruleId === 'protection/protect')
            );
            
            if (protectionErrors.length > 0) {
              const body = `## 🛡️ Protected Code Violations
              
              This PR contains changes to protected code sections that require review:
              
              ${protectionErrors.map(error => `- **${error.message}**`).join('\n')}
              
              Please ensure these changes are intentional and update the protection hashes accordingly.`;
              
              github.rest.issues.createComment({
                issue_number: context.issue.number,
                owner: context.repo.owner,
                repo: context.repo.repo,
                body: body
              });
            }

Security-Focused Protection Workflow

name: Security Code Protection
on:
  pull_request:
    paths:
      - 'src/security/**'
      - 'src/auth/**'
      - 'src/payment/**'
      - 'config/**'

jobs:
  security-protection-check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run security-focused ESLint check
        run: |
          # Only check security-related directories
          npx eslint src/security src/auth src/payment config \
            --ext .js,.ts,.jsx,.tsx \
            --rule "protection/protect: error"

      - name: Require manual approval for security changes
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            // Add security team as required reviewers
            github.rest.pulls.requestReviewers({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number,
              team_reviewers: ['security-team']
            });
            
            github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              labels: ['security-review-required', 'protected-code-changes']
            });

Matrix Testing for Protection Rules

name: Cross-Platform Protection Check
on:
  pull_request:
    branches: [ main ]

jobs:
  protection-matrix:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16, 18, 20]
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint Protection Check
        run: npx eslint . --ext .js,.ts,.jsx,.tsx
        env:
          NODE_ENV: test

Integration with Existing Workflows

You can also integrate protection checks into existing workflows:

# Add this step to your existing CI workflow
- name: ESLint Protection Check
  run: |
    echo "🛡️ Checking protected code integrity..."
    npx eslint . --ext .js,.ts,.jsx,.tsx --rule "protection/protect: error"
    
    if [ $? -eq 0 ]; then
      echo "✅ All protection checks passed!"
    else
      echo "❌ Protection violations found. Please review changes to protected code."
      exit 1
    fi

Branch Protection Rules

Configure GitHub branch protection rules to enforce protection checks:

# .github/branch-protection.yml (if using branch protection app)
protection_rules:
  main:
    required_status_checks:
      strict: true
      contexts:
        - "protection-check"
        - "security-protection-check"
    enforce_admins: true
    required_pull_request_reviews:
      required_approving_review_count: 2
      dismiss_stale_reviews: true

Real-World Use Cases

1. API Rate Limiting

//@protected 6 d4f7j2k9m1n5p8q...
function checkRateLimit(userId, endpoint) {
  const limit = RATE_LIMITS[endpoint] || 1000;
  const current = getUserRequestCount(userId, endpoint);
  return current < limit;
}

2. Feature Flags

//@protected 4 z3x7c2v9b4n8m1k...
const FEATURE_FLAGS = {
  newPaymentFlow: false,
  enhancedSecurity: true,
  betaFeatures: false
};

3. Security Headers

//@protected 7 q8w5e9r2t7y4u1i...
function setSecurityHeaders(response) {
  response.setHeader('X-Frame-Options', 'DENY');
  response.setHeader('X-Content-Type-Options', 'nosniff');
  response.setHeader('X-XSS-Protection', '1; mode=block');
  response.setHeader('Strict-Transport-Security', 'max-age=31536000');
}

VS Code Integration

For the smoothest developer experience, install the VSCode ESLint Protected extension which provides powerful automation for generating protection comments.

Key Features

The extension offers two main commands that dramatically simplify the protection workflow:

Protect Selected Code

  • Command: eslint-protect.selected
  • Keyboard shortcut:
    • Windows/Linux: Ctrl + Alt + L
    • Mac: Ctrl + Command + L
  • Behavior: Protects the selected code or current line if no selection is made

Protect Entire File

  • Command: eslint-protect.file
  • Keyboard shortcut:
    • Windows/Linux: Shift + Ctrl + Alt + L
    • Mac: Shift + Ctrl + Command + L
  • Behavior: Adds a protection comment at the top of the file to protect the entire file

Usage Example

Simply select the code you want to protect and press Ctrl + Alt + L (or use the command palette). The extension will automatically:

  1. Calculate the correct hash for your selected code
  2. Insert the properly formatted protection comment
  3. Account for the exact number of lines to protect
// Before: Select this function and press Ctrl + Alt + L
function criticalSecurityCheck(user) {
  return user.hasPermission && user.isActive;
}

// After: Extension automatically adds the protection
//@protected 3 a1b2c3d4e5f6789...
function criticalSecurityCheck(user) {
  return user.hasPermission && user.isActive;
}

Benefits

Immediate Feedback: Developers know instantly when they’ve modified protected code, rather than discovering it later in CI/CD or production.

Explicit Intent: Changing protected code requires deliberate action - updating the hash - making it impossible to modify by accident.

Team Awareness: Protection comments serve as visual indicators that code requires special attention during review.

Tool Compatibility: Works with any automated tools (AI assistants, refactoring tools, etc.) that respect ESLint rules.

Flexible Granularity: Protect individual functions, configuration blocks, or entire files as needed.

Developer Experience: The VS Code extension with keyboard shortcuts makes protecting code as simple as selecting text and pressing a key combination.

Git Integration: The Husky protection hook ensures that protective comments can’t be accidentally removed during large refactoring operations.

When to Use ESLint Protection

ESLint Protection is ideal for:

  • Authentication and authorization logic
  • Payment processing functions
  • Security configuration
  • API rate limiting rules
  • Critical business logic
  • Database connection strings
  • Feature flag definitions
  • Cryptographic functions

Conclusion

ESLint Protection bridges the gap between developer convenience and code safety. By leveraging the existing ESLint ecosystem, it provides a lightweight yet powerful way to protect critical code sections without disrupting normal development workflows.

The combination of cryptographic verification, immediate feedback, and seamless integration makes it an essential tool for any codebase where certain sections require extra protection from accidental changes.

Whether you’re building financial applications, security tools, or simply want to protect critical business logic, ESLint Protection offers a practical solution that scales with your team and codebase.


Ready to protect your critical code? Install @crabnebula/eslint-protection today and start safeguarding your most important functions from unexpected changes.