@rsuthar/state-guard-js
v1.1.0
Published
Proactive DOM attribute protection library.
Maintainers
Readme
🛡️ StateGuard.js
Keywords: dom-protection, security, mutation-observer, tamper-protection, ui-security, javascript-security, react-security, angular-security, state-guard, client-side-security
Website: StateGuard.js
Stop users from messing with your UI logic via the console.
StateGuard.js is a lightweight utility that locks DOM element attributes. If a user tries to remove a disabled attribute, change a class, or inject an onclick event via DevTools, StateGuard detects it and reverts the change in milliseconds.
🚀 Key Features
- Zero-Latency Reversion: Uses
MutationObserverfor near-instant attribute restoration. - Security Thresholds: Detects and reports repeated tampering attempts.
- Multi-Framework Support: Specialized hooks for React and Directives for Angular.
- Small Footprint: Under 2KB minified.
Core Concepts
Snapshotting: The library takes a "Source of Truth" snapshot of an element's attributes upon initialization.
The Guard: A MutationObserver watches the element 24/7. If an attribute is changed manually, it is instantly reverted to the snapshot.
Programmatic Gateway: Authorized changes must pass through the .update() method, which temporarily unlocks the element and updates the snapshot.
Installation
CDN
Include the minified version of StateGuard.js via CDN:
<script src="https://cdn.jsdelivr.net/npm/@rsuthar/state-guard-js/dist/state-guard.min.js"></script>NPM
Install the package via npm:
npm install @rsuthar/state-guard-jsYarn
yarn add @rsuthar/state-guard-jsPNPM
pnpm add @rsuthar/state-guard-jsAPI Reference
StateGuard.protect(selector, options) Initializes protection on one or more elements.
|Property|Type|Default|Description| |---|---|---|---| |selector|string|Required|"CSS selector (e.g., #btn, .admin-fields)."| |attributes|array or string,'all',"List of attributes to protect (e.g., ['class', 'disabled'])."| |maxAttempts|number,3,Number of manual changes allowed before triggering a violation.| |onViolation|function,null,Callback executed when maxAttempts is reached.|
Key Features
- Persistent State: Automatically reverts any unauthorized attribute changes.
- Threshold Alerts: Triggers security callbacks after a set number of tamper attempts.
- Framework Agnostic: Works with Vanilla JS, React, and Angular.
- Backend Sync: Built-in hooks to report "Hacker" behavior to your servers.
🛠️ Integration Guide
Vanilla JavaScript
Protect any element using standard CSS selectors.
StateGuard.protect('.secure-action', {
attributes: ['disabled', 'class', 'onclick'],
maxAttempts: 3,
onViolation: (el, count) => {
console.error("Reporting tamper attempt to server...");
// Send the report to your backend
fetch('/api/security/log-tamper', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
elementId: el.id || 'unnamed-element',
elementTag: el.tagName,
attempts: count,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
})
})
.then(() => {
if (count > 5) {
alert("Security policy violation. This session will be terminated.");
window.location.href = '/logout';
}
});
}
});
React (Using Hooks)
The hook manages the lifecycle of the observer and ensures it cleans up when the component unmounts.
const { elementRef, safeUpdate } = useStateGuard({ attributes: ['class'] });
// Use safeUpdate to modify the element without triggering the guard
const handleUnlock = () => {
safeUpdate((el) => el.classList.remove('locked'));
};
Angular (Using Directives)
Apply protection declaratively in your templates.
<button appStateGuard [protectedAttributes]="['disabled']" (violation)="logIt($event)">
Secure Action
</button>
🔒 Security Hardening Tips
- Run the Obfuscator: Always run
npm run obfuscatebefore deploying. This makes it incredibly difficult for attackers to find the "unlock" logic in your source code. - The Debugger Loop: Our production build includes a self-defending loop that triggers a
debuggerstatement if DevTools are opened while the script is running. - Backend Verification:
- Level 1: StateGuard prevents the button from being enabled.
- Level 2: Your server verifies if the user actually has the permissions to perform the action associated with that button. Never trust the client alone.
🧪 Testing the Guard
To verify that the protection is active, run the included Jest suite:
npm test
This suite simulates a user attempting to delete attributes via the console and confirms the library restores them within the 100ms threshold.
Release Notes
Here are the official Release Notes for your version 1.0.0 launch. You can paste these directly into the "Releases" section of your GitHub repository.
**🚀 Release v1.0.0: StateGuard.js Initial Launch We are excited to announce the first stable release of StateGuard.js, a proactive security utility designed to harden client-side UI logic and prevent manual DOM tampering.
🌟 Features
- Persistent Attribute Guard: Uses MutationObserver to instantly revert unauthorized changes to disabled, class, readonly, and more.
- Framework First: Includes native support for React (hooks) and Angular (directives).
- Security Thresholds: Customizable maxAttempts logic to detect and log repeated tampering behavior.
- Automated Pipeline: Integrated GitHub Actions for automated testing and JavaScript Obfuscation.
- Reporting Hook: Seamless onViolation callback for backend integration and audit logging.
📦 Installation Download the dist/state-guard.obf.js for production.
Initialize with StateGuard.protect(selector, options).
🛡️ Best Practices for this Version Obfuscate always: Never use the src/ files in a public production environment.
Update Gateway: Always use the .update() or safeUpdate methods to modify protected elements programmatically to avoid "infinite revert" loops.
Next Steps for You
- Initialize the project: Run
npm init -yin your folder and paste thepackage.jsoncontent. - Install dependencies: Run
npm install. - Build: Run
npm run buildto generate your minified file.
Would you like me to help you write a sample GitHub Action script so that this library is automatically tested and obfuscated every time you push code to your repository?
