~/sang

Log Detective: a small game for learning to read logs

Reading logs is mostly learning what normal looks like, so that you notice the one line that is not. I wanted a small way to practice that, so I built Log Detective: you get six lines of a fictional server log and 15 seconds to find the malicious one. It is an easter egg on this site. If you go looking, the terminal knows the way.

How it plays

A game is 10 cases drawn at random from 18, with three lives. A correct answer scores 100 points, plus 10 for every second left on the clock, plus a streak bonus that grows by 25 for each correct answer in a row, up to 100. A wrong answer or a timeout costs a life and resets the streak.

After every answer the game marks both your pick and the right line, using text as well as colour, then explains what gave the line away and what the fix would be. Reading that explanation is the point, so even when you lose your last life, the game waits for you to move on before it ends.

The cases

The 18 cases fall into five groups:

Writing a case is harder than it looks

A case needs exactly one defensible answer. The brute-force case shows why. Several identical failed-login lines are all equally suspicious, so asking “which line is malicious?” has no single answer. The question has to be “which line shows the attacker getting in?”, and the answer is the one Accepted line at the end.

Decoys matter too. The scanner case puts a real Googlebot among ordinary browsers, so the clue is the sqlmap string and not just an unfamiliar name. The right answer’s position is spread across all six slots, and a test checks that, so position gives nothing away.

Real logs are much noisier than these. Each case has one clear signal, which makes the game a warm-up for the skill and not a simulation of it.

Fictional on purpose, and checked

Every IP address comes from the ranges reserved for documentation (192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24), and every hostname uses example.com, example.net, example.org or .test. There are three exceptions. One is 169.254.169.254, the cloud metadata address, which is real and is the whole point of that case. The other two are real names that tools announce themselves with: sqlmap’s website and Googlebot’s URL.

A unit test pulls every IP address and hostname out of every case and fails on anything else. I checked that it works by planting a real-looking address and a company domain in a case, and it failed with a clear message. That way no case can accidentally point at a real server or look like a real company’s traffic.

Text, never markup

Logs are full of characters that HTML treats as special. The XXE case contains a literal <x>&f;</x>. Every line is written to the page as text and never parsed, and a browser test plays a whole game checking that no line ever creates an element. A game about injection should not be injectable.

The loophole I shipped

Log Detective runs on the same small game shell as Tetris and Snake, which provides starting, pausing and pausing when you leave the window. I had written that pausing “hides the log” and had even named a test that way. Then I took a screenshot of the paused game for this post.

The overlay was only 82% opaque. The question and all six lines, including the answer, were plainly readable through it. You could read the whole case with the clock stopped, press P and answer instantly, so the timer meant nothing. A screen reader could read the lines the same way. The test I had named “hides the log lines behind the pause overlay” never actually checked that.

The fix is to hide everything on the board whenever the overlay is up: visually, from assistive technology and from the Tab order. That created a second bug. Hiding a line that has keyboard focus drops focus to the page, so the key that resumes the game stopped working. The shell now moves focus to the board first. Both fixes have tests, and I removed each fix in turn to check that the right test failed.

Looking at the screenshots caught another problem the tests had missed. A tall window opened far enough down that its bottom edge, including the Next button, was clipped off the workspace, and no amount of scrolling could reach it. Windows now start higher when they need to.

What it is built on

The rules, scoring and case data are plain TypeScript with no DOM, so the unit tests exercise them directly. This is the first of the three games built from ordinary page elements (buttons and text) instead of a canvas, and the shell’s pause, pause-on-blur and best-score handling worked unchanged. Your best score is stored only in your own browser.

Limits

Eighteen cases means you will see repeats after a few games. I wrote the explanations from general knowledge, so check the details yourself before relying on them, for example the Log4Shell CVE number. And I have tested it on emulated phones, not real ones.

← all posts

guest@sang:~$