Lightweight runtime security agent that monitors web pages for unauthorized DOM modifications, script injection, and suspicious attribute mutations — in real time.
▶ Live Demo · Suite · API · Compatibility · Release notes · Security · Tests
FrontGuard Agent drops into any web page as a single <script> tag and watches for the things attackers actually do:
- Script injection — third-party scripts loaded outside an allowlist (e.g. supply-chain attacks, malicious Chrome extensions, compromised npm packages)
- Iframe injection — invisible trackers, clickjacking overlays, phishing frames
- DOM tampering — suspicious attribute mutations like
onerror,onclick,srcdocadded at runtime - Container injection — scripts hidden inside dynamically inserted DOM subtrees
Every detection produces a structured event: type, severity, timestamp, URL, and details. Events can be streamed to a backend, logged to console, or handled with a custom callback.
This is the same category of tool as Cloudflare Page Shield and Datadog RUM — focused on a single, well-scoped problem: detecting unauthorized client-side activity.
It is also the runtime layer of the FrontGuard Suite: the playground teaches the vulnerability, the agent detects the production behavior, and the roadmap points toward event ingestion and a security triage dashboard.
Build from source:
npm install
npm run buildDrop the built IIFE bundle into any HTML page:
<script src="/dist/frontguard.iife.js"></script>
<script>
FrontGuard.init({
scriptAllowlist: ['cdn.trusted.com', 'analytics.example.com'],
onEvent: (event) => {
console.warn('[FrontGuard]', event.type, event.severity, event.details);
// Or: navigator.sendBeacon('/security/events', JSON.stringify(event))
},
});
</script>Or consume the ESM build from a workspace/package install:
import FrontGuard from 'frontguard-agent';
const agent = FrontGuard.init({
scriptAllowlist: ['cdn.trusted.com'],
onEvent(event) {
navigator.sendBeacon('/security/events', JSON.stringify(event));
},
});
// Later, if this instance is route-scoped:
agent.stop();That's it. The agent runs in the background, snapshotting the trusted DOM at startup and flagging relevant runtime changes.
The build emits:
| File | Purpose |
|---|---|
dist/frontguard.es.js |
ESM import for apps, bundlers, and package consumers |
dist/frontguard.iife.js |
Browser global build exposed as window.FrontGuard |
dist/types/ |
TypeScript declarations for the public API |
See API.md for config, event schemas, lifecycle methods, and telemetry examples.
Release readiness is tracked in RELEASE_CHECKLIST.md, with release history in CHANGELOG.md and security reporting in SECURITY.md.
The demo/ directory contains an interactive page where you can trigger real attacks (script injection, iframe injection, attribute tampering, nested-container injection) and watch the agent detect each one in real time.
The hosted demo also sends detected events to the FrontGuard Suite triage prototype by default. Open the matching dashboard stream at frontguard-nine.vercel.app/security-events?orgId=frontguard-labs&projectId=agent-demo&appId=frontguard-agent-demo. If the receiver enables scoped write tokens, paste the token into the demo's Write Token field.
npm run build
npx serve .
# → open http://localhost:3000/demo/The agent is split into focused modules so each one is small enough to reason about and test independently.
frontguard-agent/
├── src/
│ ├── index.ts # Public API: FrontGuard.init({ ... })
│ ├── dom-monitor.ts # MutationObserver-based detection
│ └── types.ts # Shared types: SecurityEvent, FrontGuardConfig
├── tests/
│ └── dom-monitor.test.ts
└── demo/
└── index.html # Interactive attack simulatorOn init(), the agent snapshots every <script> and <iframe> already on the page into a WeakSet — these are considered trusted. After that, a MutationObserver watches the entire document subtree for childList and attributes mutations.
When a new element is added, the agent checks whether it's a script or iframe (or contains one in its subtree) and, if it isn't in the trusted set, emits an event. Once flagged, the element is added to the trusted set so subsequent mutations to the same node don't double-count.
WeakSet is deliberate: when a flagged element is removed from the DOM and garbage collected, the agent's reference to it goes away too. No memory leaks, even on long-running pages.
interface SecurityEvent {
type:
| 'dom.script-injected'
| 'dom.iframe-injected'
| 'dom.suspicious-attribute';
severity: 'low' | 'medium' | 'high' | 'critical';
timestamp: number;
url: string;
details: Record<string, unknown>;
}- Critical — script injection from outside the allowlist (highest impact: arbitrary code execution)
- High — iframe injection, suspicious attribute additions (
onerror,onclick, etc.) - Low — script from an allowlisted domain (informational; still surfaced for audit trails)
FrontGuard.init({
scriptAllowlist?: string[]; // Domains permitted to load scripts
onEvent?: (event) => void; // Custom event handler
disabled?: boolean; // Disable in dev
});The returned instance supports:
const agent = FrontGuard.init();
agent.getEvents(); // readonly SecurityEvent[]
agent.stop(); // disconnect the MutationObserverFor browser support, CSP notes, privacy posture, and known limitations, see COMPATIBILITY.md.
npm testThe test suite covers the core detection paths using vitest + jsdom:
- ✅ Flags scripts injected after init
- ✅ Flags iframes injected after init
- ✅ Does not flag scripts that existed at init time (baseline trust)
- ✅ Respects the script allowlist (low severity for trusted CDNs)
- ✅ Flags scripts nested inside an injected container
- ✅ Covers the public API lifecycle:
init,disabled,stop,getEvents, and the debug handle
Each test uses afterEach to disconnect its MutationObserver — without this, observers from previous tests remain alive and cross-contaminate. (Real bug. Real fix. Real lesson.)
npm run sizeThe IIFE bundle is under 2KB gzipped at the time of writing, with headroom budgeted for the script and network monitors.
Before publishing or creating a GitHub release, run the release checklist.
- Script execution monitor — hook
document.createElementto catch scripts created via JS - Network monitor — wrap
fetchandXMLHttpRequestto flag requests to non-allowlisted domains - Telemetry reporter — batched event delivery via
navigator.sendBeaconwith WebSocket fallback for real-time streaming - Source map for stack traces on injected script detection
- CSP report-only ingestion — correlate with native browser CSP reports
- Suite ingestion demo — hosted demo events can POST into the FrontGuard triage prototype
FrontGuard Playground — the educational counterpart to this agent. The playground demonstrates the vulnerabilities (XSS, broken auth, missing RBAC, DevTools bypass); this agent is the production tool for detecting them in the wild. The shared product roadmap is documented in FrontGuard Suite.
MIT — Zoriah Cocio · zoriahcocio.com · info@zoriahcocio.com
