# Traps

Failures that cost a run, with the measurement that identifies each one. The first group is
environmental — they belong to Linux, systemd and package management. The second belongs to the
tool itself and to writing shell that must not lie.

## Environment traps

| Trap | Symptom | Action |
|---|---|---|
| The patched file does not patch the process | `dpkg -l` is new, the decoder in memory is old | compare inodes in `/proc/*/maps`, never infer from the package database |
| `set -o pipefail` with `head -1` or `awk '…exit'` | exit 141 on a successful command | omit `pipefail`; use explicit checks for correctness |
| `set -euo` where `set -eu` was meant | the shell lists its options and exits | `-o` requires an argument; a dangling `-o` is a listing request |
| Sourcing a `key=value` file | syntax error on a value containing parentheses or spaces | parse by key |
| Inode captured from a glob | the symlink inode is recorded; the stale check can never fail | `find -type f -printf '%i'` |
| `printf '%s'` without a newline in a loop | names concatenate; a restart addresses one nonexistent unit | `printf '%s\n'` |
| A `for`/`grep` loop under `set -e` | exit 1 after the work has already been done | guard the loop with `\|\| true` and decide from the exit-code contract |
| Panel daemons started as transient scopes | no unit to restart, `PPID=1`, survives every `systemctl` | restart through the panel control tool (`mgrctl --restart`) |
| `needrestart` on Ubuntu | services restart during installation, the explicit step finds nothing | state that the hook may have acted; verify inodes afterwards |
| Desktop stack maps the library | `gnome-shell`, `Xorg`, `fwupd`, VPN client appear vulnerable | classify as deferred; do not restart a session |
| Template units (`revoap-worker@1`) | the cgroup regex sees no unit | take the innermost `.service` segment of the cgroup path |
| A pre-existing failure appears right after the change | `logrotate` fails on an unrelated site config | report it, do not repair it inside a security change |
| Distribution revision carries the fix | the upstream number still looks vulnerable | make the packaged changelog the authority |
| An end-of-life runtime | every package reports patched, the bundled decoder is still vulnerable | Node.js 20.x, 23.x and 25.x have no fix: migration is the remediation |
| A framework that decompresses by default | an endpoint that never advertised Brotli decodes it anyway | Express 5 and `body-parser` 2 default `inflate` to `true` for JSON, raw, text and URL-encoded bodies |
| Trusting the control plane | Istio is upgraded, the proxies still run the old Envoy | check the proxy version and the `EnvoyFilter` list, not the CRD version |
| Changelog endpoint answers 404 on publish day | the version suffix cannot be confirmed remotely | extract the `.deb` and read the shipped changelog |

## Tool traps

| Trap | Symptom | Action |
|---|---|---|
| Running `remediate.sh` first | exit 10, no state to compare against | always `detect.sh` first |
| Treating exit 1 as one condition | `detect.sh` and `verify.sh` both use 1 for opposite answers | branch on the script that produced it |
| Editing `detect.sh` to "fix" a host | a read-only script that writes | the detector must stay read-only; move the action to the remediator |
| Widening the desktop denylist by guesswork | a real server consumer is skipped | every addition needs a measured case |
| Repeating `remediate.sh` on a patched host | exit 10: candidate equals installed | re-run `detect.sh` instead; a clean `SAFE` is the confirmation |
| Restarting a hosting panel with a signal | panel state corruption | `mgrctl --restart srvparam` |
| Ignoring `systemctl --failed` | a restart casualty stays broken and unrecorded | the verifier treats it as a failure |

## Traps in the tooling that publishes this repository

Two defects of the documentation pack itself, measured while preparing this repository. They are
recorded because the next maintainer will meet them again.

| Trap | Symptom | Action |
|---|---|---|
| The pack's README summary rule reads the first non-empty line after the `H1` | the language-switcher comment (`<!-- md-i18n:switcher -->`) sits there, starts with `<`, and the check reports `readme.summary` as failed — on the pack's own README too | put the summary prose immediately after the `H1` and the switcher block below it |
| The wiki generator hardcodes Russian navigation labels (`tools/ghalib/wiki.py:248`) | an English-only wiki gains a `### Ссылки` group with `Репозиторий` and `Релизы` | correct `_Sidebar.md` after the build, or take the labels from the manifest once the generator supports it |
| The anchor slug of the checker drops combining marks | Devanagari anchors from a table of contents are reported missing although the generated slug is correct | the Hindi contents list ships unlinked rather than carrying anchors that may fail in a browser |

## Writing documentation about this CVE

| Trap | Consequence | Action |
|---|---|---|
| Quoting the upstream version only | a patched host reads as vulnerable | quote the full distribution version and the pocket |
| Quoting a CVSS number without its scale | 9.2 and 8.5 are both correct and look contradictory | name the scale and the source |
| Searching only for "brotli" | write-ups using Node2Shell or HTTP2Shell are missed | search the identifier and the aliases |
| Publishing a hostname or address in a case study | a real infrastructure leak in a defensive tool | anonymise hosts, keep the technical detail |
| Recording a snippet as a verified fact | the page ages into a rumour | mark the evidence level next to the claim |

## Why these are written down

Each row above was paid for. The three that produced a false green — the missing newline, the symlink
inode, and the assumption that a package update reaches running processes — are the reason the tool
verifies effect rather than intent. A defect that reports success is more expensive than one that
crashes, because it closes an incident that is still open.

Back to [Home](Home).
