Skip to content

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 ​

mermaid
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 route
  • 10.0.0.0/8 — RFC 1918 Private address space
  • 100.64.0.0/10 — RFC 6598 Carrier-Grade NAT (CGNAT) / Shared address space
  • 127.0.0.0/8 — Loopback interfaces
  • 169.254.0.0/16 — Link-local network (including cloud instance metadata 169.254.169.254)
  • 172.16.0.0/12 — RFC 1918 Private address space
  • 192.0.0.0/24 — IETF Protocol Assignments
  • 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 — TEST-NET-1, TEST-NET-2, TEST-NET-3 documentation ranges
  • 192.168.0.0/16 — RFC 1918 Private address space
  • 198.18.0.0/15 — Benchmarking / device interconnect subnets
  • 224.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 interface
  • fc00::/7 — RFC 4193 Unique Local Addresses (ULA)
  • fe80::/10 — Link-local unicast
  • ff00::/8 — Multicast
  • 2001: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:

js
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 API api.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):

mermaid
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):

js
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 finally block 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 /admin category 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 Set before dispatch.
  • Server Permissions: Commands requiring server management check Discord guild permissions (MANAGE_GUILD or ADMINISTRATOR) on the caller.

Obsidian-engineered Discord utility platform & temporary intelligence engine.