A production-grade QR code detection and security analysis system delivered as both a Chrome/Edge browser extension and a mobile-friendly web app.
- AI-powered content classification (URL, email, phone, crypto, WiFi, geo, vCard, etc.)
- Risk scoring engine (0–100 scale) with human-readable explanations
- Color-coded risk badges: green (SAFE), yellow (SUSPICIOUS), red (HIGH RISK)
- "Why is this risky?" expandable accordion on every result
- Strict Mode toggle for lower risk tolerance
- Safe-open confirmation for HIGH RISK URLs
- Mock report action (ready for real threat intel integration)
- Shared analysis modules between extension and web app
qr-detector-extension/
├── manifest.json Extension config (MV3)
├── background.js Service worker — tab state, badge, injection
├── content.js Thin bootstrap — module init only
├── qrDecoder.js Image preprocessing + jsQR decode (12 strategies)
├── analyzer.js NEW — content classifier + AI adapter interface
├── riskEngine.js NEW — 0–100 risk scorer + explanations
├── scanner.js QR extraction pipeline (img/canvas/svg/video/shadow/iframe)
├── ui.js Risk-aware panel UI
├── styles.css Namespaced styles, dark mode, responsive
├── jsQR.min.js QR decode library (download required)
├── icons/ Extension icons (16/48/128px)
├── webapp/
│ ├── index.html Mobile-friendly web app
│ ├── app.css Web app styles
│ └── app.js Upload + camera + results controller
└── tests/
└── analyzer.test.js Example test cases with expected outputs
| File | Responsibility |
|---|---|
analyzer.js |
Classify QR content type, extract URL features, AI adapter interface |
riskEngine.js |
Score 0–100, produce reasons + recommendations, strict mode |
scanner.js |
Scan all DOM sources, feed through analysis pipeline, emit events |
ui.js |
Render risk badges, accordion, actions — no business logic |
content.js |
Bootstrap only — wait for modules, call init |
| Band | Score | Color |
|---|---|---|
| SAFE | 0–30 | 🟢 Green |
| SUSPICIOUS | 31–70 | 🟡 Yellow |
| HIGH RISK | 71–100 | 🔴 Red |
- URL shortener detected (bit.ly, tinyurl, etc.) +30
- IP-based URL +25
- No HTTPS +15
- Suspicious TLD (.tk, .ml, .xyz, etc.) +20
- IDN homograph / non-ASCII domain +35
- Excessive query parameters (>5) +15
- High path entropy (obfuscated path) +20
- Deep subdomain structure +15
- Non-standard port +10
- Phishing keywords (login, verify, account, etc.) +40
- Known blocklisted domain +60
- WEP WiFi encryption +25
- HTML/JS data URI +55
- Control characters in data +30
analyzer.js exposes a pluggable adapter interface:
// Example: plug in an OpenAI or local ONNX model
QRAnalyzer.setAIAdapter({
async analyze(data, classification) {
const response = await fetch('/api/classify', {
method: 'POST',
body: JSON.stringify({ data, classification }),
});
return response.json(); // { label, confidence, explanation }
}
});The adapter is called after rule-based analysis and its output can augment the risk score. No external calls are made without explicitly setting an adapter.
- Download jsQR:
curl -o jsQR.min.js https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js - Open
chrome://extensions, enable Developer Mode - Click "Load unpacked" → select this folder
Open webapp/index.html in any modern browser. No server required — runs entirely client-side.
For camera access on mobile, serve over HTTPS (required by browsers):
npx serve .
# then open http://localhost:3000/webapp/See tests/analyzer.test.js for 15 documented test cases covering:
- Safe HTTPS URLs
- URL shorteners
- IP-based phishing URLs
- IDN homograph attacks
- Crypto addresses
- WiFi (WPA/WEP)
- Data URIs with embedded scripts
- Plain text, geo, email
Load the test file in a browser console alongside analyzer.js and riskEngine.js to run them.
- Zero network calls — all analysis is local-first
- No QR data ever leaves the browser
- All decoded data rendered via
textContent(neverinnerHTML) — XSS safe - AI adapter requires explicit opt-in by the developer
- Minimal permissions:
activeTab,scripting,storage
- Chrome 88+, Edge 88+, Brave, Opera
- Web app: any modern browser with
getUserMediafor camera - Firefox: requires minor manifest adjustments (MV2 or MV3 with polyfill)