Security Engine & Hardening
June is designed under zero-trust assumptions. Any input from Discord users, external URLs, or network queries is treated as potentially adversarial until validated, sanitized, and isolated.
1. Zero-Trust SSRF Defense Firewall
All features making outbound HTTP requests (URL previews, webpage screenshots, metadata inspectors) pass through June’s network firewall (src/utils/network.js).
Multi-Stage Validation Pipeline
flowchart TD
Raw["Raw User Target / URL"] --> Parse["URL Parser & Protocol Check\n(Strict http/https, no credentials)"]
Parse --> NormHost["Host Normalization\n(Strip trailing dot, IPv6 brackets, localhost check)"]
NormHost --> DNS["DNS Resolution via dns.lookup\n(verbatim: true, IPv4 & IPv6 records)"]
DNS --> RangeCheck{"Blocked IP Check\n(Private, CGNAT, Loopback, ULA, NAT64, 6to4)"}
RangeCheck -->|Matches Blocklist| Reject["Immediate Rejection\n'private or reserved targets are blocked'"]
RangeCheck -->|All IPs Public| Pin["Socket IP Pinning\n(Custom lookup: target.address, target.family)"]
Pin --> Dispatch["Dispatch Request via Node http/https\n(Stream cap & timeout watchdog)"]Complete Range Enforcement
The firewall validates every resolved IP address against the complete set of private, link-local, documentation, and reserved ranges:
IPv4 Range Blocklist
0.0.0.0/8— Current network / default route10.0.0.0/8— RFC 1918 Private address space100.64.0.0/10— RFC 6598 Carrier-Grade NAT (CGNAT) / Shared address space127.0.0.0/8— Loopback interfaces169.254.0.0/16— Link-local network (including cloud instance metadata169.254.169.254)172.16.0.0/12— RFC 1918 Private address space192.0.0.0/24— IETF Protocol Assignments192.0.2.0/24,198.51.100.0/24,203.0.113.0/24— TEST-NET-1, TEST-NET-2, TEST-NET-3 documentation ranges192.168.0.0/16— RFC 1918 Private address space198.18.0.0/15— Benchmarking / device interconnect subnets224.0.0.0/4— Multicast (Class D)240.0.0.0/4— Reserved for future use (Class E)
IPv6 Range Blocklist
::/128— Unspecified address::1/128— Loopback interfacefc00::/7— RFC 4193 Unique Local Addresses (ULA)fe80::/10— Link-local unicastff00::/8— Multicast2001:db8::/32— Documentation subnet::ffff:0:0/96— IPv4-mapped IPv6 (embedded IPv4 checked against IPv4 rules)64:ff9b::/96— Well-Known Prefix for NAT64 (embedded IPv4 checked against IPv4 rules)2002::/16— 6to4 encapsulation prefix (embedded IPv4 checked against IPv4 rules)
Socket IP Pinning (TOCTOU & Rebinding Defense)
Standard HTTP clients re-resolve hostnames when opening sockets, introducing Time-of-Check to Time-of-Use (TOCTOU) DNS rebinding vulnerabilities. June prevents this by overriding the underlying socket resolver:
const request = transport.request({
protocol: target.url.protocol,
hostname: target.url.hostname,
port: target.port,
path: `${target.url.pathname}${target.url.search}`,
// Pin connection directly to pre-validated IP:
lookup: (_hostname, _lookupOptions, callback) => callback(null, target.address, target.family),
agent: false
});Because the socket connects directly to target.address while preserving the requested HTTP Host and TLS servername, DNS rebinding is prevented.
Port Policy & Documented Exceptions
- General HTTP Pipeline: Strictly restricts outbound ports to 80 (HTTP) and 443 (HTTPS). Non-standard ports (e.g.
8080,3000,6379,22) are rejected at the normalization layer. - Specialized Diagnostic Exceptions:
/minecraft server: Queries Minecraft server statuses. Instead of making raw socket connections, June queries the dedicated status APIapi.mcsrvstat.us, allowing arbitrary Minecraft ports without opening internal network holes./portscan: An administrator-only diagnostic tool that conducts probe-limited TCP socket connections across a fixed set of 11 common ports against public hosts, with mandatory SSRF IP validation and explicit authorization confirmation.
2. ReDoS Mitigation & Worker Thread Sandboxing
Regular expression denial of service (ReDoS) occurs when malicious input forces an engine with exponential or polynomial backtracking into catastrophic recursion.
June mitigates ReDoS using isolated Node.js worker_threads (src/utils/runRegex.js):
flowchart LR
App["June Main Event Loop"] -->|Dispatch pattern & input| Worker["Node.js Worker Thread\n(regexWorker.js)"]
Timer["500ms Watchdog Timer\n(setTimeout)"] -.->|Exceeded| Terminate["worker.terminate()\nReject with Timeout"]
Worker -->|Matches Found| Success["Resolve Matches\nworker.terminate()"]Sandbox Safeguards:
- Length Caps: Patterns are limited to $\le 256$ characters; test inputs are limited to $\le 8,000$ characters.
- Flag Sanitization: Only valid standard regex flags (
dgimsuvy) without duplicates are permitted. - Thread Isolation: Pattern execution occurs in a separate Node.js worker thread, protecting the main event loop.
- 500ms Software Watchdog: If evaluation exceeds 500ms, the worker thread is terminated via
worker.terminate(), preventing thread starvation.
3. Cryptographic Timing-Safe Comparisons
To eliminate side-channel timing leaks when verifying administrative tokens, webhook secrets, and session cookies, June uses HMAC-then-compare cryptographic routines (crypto.timingSafeEqual):
function constantTimeCompare(a, b) {
if (typeof a !== 'string' || typeof b !== 'string') return false;
const key = crypto.randomBytes(32);
const hmacA = crypto.createHmac('sha256', key).update(a).digest();
const hmacB = crypto.createHmac('sha256', key).update(b).digest();
return crypto.timingSafeEqual(hmacA, hmacB);
}Hashing both inputs with an ephemeral HMAC key before comparing guarantees fixed 32-byte digests, eliminating both character-by-character timing variances and input length leaks.
4. Media & Forensics Sanitization
When processing images for OCR, EXIF forensics, or avatar inspection:
- Metadata Stripping: Sensitive user metadata (such as GPS coordinates, camera serial numbers, and personal timestamps) is extracted only when explicitly requested via
/exif, and never reflected in general embeds. - Payload Decompression Caps: Image download streams enforce a strict 10MB byte cap (
maxBytes), protecting against memory exhaustion denial-of-service ("decompression bombs"). - Isolated File Handling: Media temporary files are allocated using random UUIDs and unlinked within a
finallyblock post-processing.
5. Privilege & Whitelist Hierarchy
June implements a strict dual-tier access control system:
[Global Operator / Admin (OWNER_ID)]
│
▼
[Whitelisted Users (SQLite admins / whitelist)]
│
▼
[Server Context (Installed Guilds & Permitted Roles)]
│
▼
[Public Discord Users (Restricted Slash Commands)]- Operator Layer: Commands in the
/admincategory are hard-gated to the bot owner and SQLite-configured administrators. - Whitelisted Users: Private diagnostic and intelligence features check user IDs against an in-memory
Setbefore dispatch. - Server Permissions: Commands requiring server management check Discord guild permissions (
MANAGE_GUILDorADMINISTRATOR) on the caller.