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:
- Add the comment without a hash:
//@protected 3
- Run ESLint - it will report an error with the correct hash
- 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:
- Make all your code changes first
- Run ESLint to see all hash mismatches
- 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
- Create a dedicated branch:
feature/protected-auth-update
- Document the change reason: Link to tickets, requirements, or security reviews
- Make minimal changes: Only modify what’s absolutely necessary
- Update protection hashes: Run ESLint and update all affected hashes
- Request additional review: Tag security/compliance team members
- Verify in staging: Test protected code changes thoroughly
- 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:
- ESLint Protection: Catches hash mismatches during linting
- 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
- Windows/Linux:
- 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
- Windows/Linux:
- 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:
- Calculate the correct hash for your selected code
- Insert the properly formatted protection comment
- 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.