โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
       โ”‚   h4ck0r.com  ::  field notes โ”‚
       โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
    

bugs, constraints, and what they left behind. written between deploys.

engineering notes from the build โ€” kept by h4ck0r.com.


2026-07-26why_these_notes_exist

the toolkit on the front page shows what works. this page is the other half: what didn't, and why.

every entry here is a real defect or a real constraint hit while building and running this site โ€” the kind of thing that costs an afternoon once and five minutes forever after. no roadmap, no announcements, no marketing. if it isn't a lesson, it doesn't go in.


2026-07-09port_43_is_closed

the WHOIS tool was written the obvious way: open a socket to the registry on tcp/43, send the domain, read the reply. on this host it hung until the timeout, every time.

# egress policy, /etc/csf/csf.conf
TCP_OUT = "20,21,22,25,53,80,110,113,443,465,587,853,993,995,2222,3306"

43 isn't in the list, so the connection never leaves the box. the firewall doesn't reject it โ€” it drops it, which is why the symptom is a hang and not an error. worse, exec() and shell_exec() are disabled here, so the usual escape hatch of shelling out to the whois binary was closed too.

the fix was to stop treating whois as a protocol problem. every gTLD registry now publishes RDAP โ€” the same registration data as structured JSON over plain HTTPS. one curl call to 443, no firewall exception, and a parseable response instead of forty registrar-specific text formats.

the lesson: when an outbound connection hangs rather than fails, suspect egress filtering before you suspect the remote end. and check whether the modern replacement protocol already speaks over 443 โ€” usually it does.


2026-05-21the_collation_trap

two text columns from different tables. a UNION over them. one's utf8mb4_unicode_ci, the other's utf8mb4_general_ci. mariadb won't pick a side.

ERROR 1271: Illegal mix of collations for operation 'UNION'

but the API's try / catch swallows the error and returns []. the UI renders a clean, empty report card. so the fallback object gets "fixed", it ships, and the real bug surfaces six minutes later.

the lesson isn't "check collations." it's an empty array is sometimes a scream. a catch block that returns a valid-looking shape converts an outage into a silent wrong answer.


2026-05-21leftmost_vs_rightmost

three places in a codebase read X-Forwarded-For. one took the leftmost entry. the other two โ€” fixed in a prior pass โ€” took the rightmost.

leftmost is whatever the client sent, and a client can send anything. rightmost is what your own proxy appended. the difference is who you trust.

an automated scan called it "account takeover via 2FA brute force." it wasn't: the rate limiter upstream already used the safe helper. the actual impact was session-log poisoning โ€” a forensic problem, not an auth bypass.

fixed anyway, because a log you can forge is a log you can't use in an incident. the bigger lesson: scanners overcall. reproduce the finding before you patch in a panic, or you'll spend the day hardening something that was never broken.


2026-05-21room_joins_need_authz

a websocket server, freshly hardened for MFA, reviewed the same week. the auth on connect was solid. the room subscription underneath it was not:

// before
socket.on("event_client_open_chatbox", (data) => {
  const cid = parseInt(data?.conversation_id);
  if (cid) socket.join(`conversation_${cid}`);   // โ† no membership check
});

any authenticated user could join any conversation room by guessing an integer, then passively receive every message published to it. authentication was never the missing piece โ€” authorisation was, and the two get conflated constantly on socket transports because the handshake feels like the gate.

patched by checking membership server-side before join(). the lesson that keeps recurring: the code shipped most recently is the dirtiest code in the repo. audit what you just touched, not what you touched last year.

more when something breaks in an interesting way